Cavern.sigma
Welcome to Cavern.sigma
已經不敢寫時間了笑死。 這篇裡面用了 `<details>` 來做補充說明,藍藍的斜體字應該要可以按開 另外這題太長ㄌ,所以分成兩篇寫 # passcode 他的 code 長這樣 ```c #include <stdio.h> #include <stdlib.h> void login(){ int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2); printf("checking...\n"); if(passcode1==338150 && passcode2==13371337){ printf("Login OK!\n"); system("/bin/cat flag"); } else{ printf("Login Failed!\n"); exit(0); } } void welcome(){ char name[100]; printf("enter you name : "); scanf("%100s", name); printf("Welcome %s!\n", name); } int main(){ printf("Toddler's Secure Login System 1.0 beta.\n"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; } ``` 第一個發現可以操作的地方是 `welcome` 跟 `login` 的連續呼叫 我們知道呼叫函式會打開一個 stackframe ,在函式回傳的時候 stackframe 會被棄置 而函式內的變數是放在 stackframe 裡面的,所以這裡的連續呼叫就可能可以下手 `welcome` 裡面的 `name` 是一個 `char[]` 而且在下面的 `scanf()` 讀進來 也就是說這個 `name` 內容是可以操作(用輸入的字元來佈置)的 `login` 裡面的兩個變數 `passcode1` 跟 `passcode2` 沒有給過值,所以他們會是在 `login` 的 stackframe 展開之後的垃圾 運氣好的話(如果 `name` 跟 `passcode1` `passcode2` 有重疊),我們有機會利用 `name` ,在裡面放設計好的值藉此污染 `passcode1` `passcode2` ,讓他們變成任意我們設定的值 有了這樣的想法之後,我們來實際看看這個效果能不能被實現 首先我們要找出 `name` `passcode1` `passcode2` 的位置在哪裡 因為這些東西都在 stack 上,所以要看他們和 `sp` 或者 `bp` 的相對位置(也就是在 stackframe 中的位置),絕對位置是會隨著 `bp` 偏移的 我們可以透過看函式的參數來知道位置,下面用 gdb-peda 來做這件事 進入 gdb ```shell $ gdb passcode ``` 我們要看 `name` 的位置,可以選 `scanf("%100s", name);` 這行的指令來看 所以 `disas` (disassemble 的意思) ```shell gdb-peda$ disas welcome Dump of assembler code for function welcome: 0x08048609 <+0>: push ebp 0x0804860a <+1>: mov ebp,esp 0x0804860c <+3>: sub esp,0x88 0x08048612 <+9>: mov eax,gs:0x14 0x08048618 <+15>: mov DWORD PTR [ebp-0xc],eax 0x0804861b <+18>: xor eax,eax 0x0804861d <+20>: mov eax,0x80487cb 0x08048622 <+25>: mov DWORD PTR [esp],eax 0x08048625 <+28>: call 0x8048420 <printf@plt> 0x0804862a <+33>: mov eax,0x80487dd 0x0804862f <+38>: lea edx,[ebp-0x70] 0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx 0x08048636 <+45>: mov DWORD PTR [esp],eax 0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt> 0x0804863e <+53>: mov eax,0x80487e3 0x08048643 <+58>: lea edx,[ebp-0x70] 0x08048646 <+61>: mov DWORD PTR [esp+0x4],edx 0x0804864a <+65>: mov DWORD PTR [esp],eax 0x0804864d <+68>: call 0x8048420 <printf@plt> 0x08048652 <+73>: mov eax,DWORD PTR [ebp-0xc] 0x08048655 <+76>: xor eax,DWORD PTR gs:0x14 0x0804865c <+83>: je 0x8048663 <welcome+90> 0x0804865e <+85>: call 0x8048440 <__stack_chk_fail@plt> 0x08048663 <+90>: leave 0x08048664 <+91>: ret End of assembler dump. ``` 我們很快可以找到呼叫 `scanf` 的那一行,然後用 `b` 來下斷點 ```shell gdb-peda$ b *0x08048639 ``` 弄好了之後讓他跑起來 ```shell gdb-peda$ r [----------------------------------registers-----------------------------------] EAX: 0x80487dd ("%100s") EBX: 0x0 ECX: 0x0 EDX: 0xff9a2a58 --> 0xf7f5cd80 --> 0xfbad2a84 ESI: 0xf7f5c000 --> 0x1d7d8c EDI: 0x0 EBP: 0xff9a2ac8 --> 0xff9a2ae8 --> 0x0 ESP: 0xff9a2a40 --> 0x80487dd ("%100s") EIP: 0x8048639 (<welcome+48>: call 0x80484a0 <__isoc99_scanf@plt>) EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804862f <welcome+38>: lea edx,[ebp-0x70] 0x8048632 <welcome+41>: mov DWORD PTR [esp+0x4],edx 0x8048636 <welcome+45>: mov DWORD PTR [esp],eax => 0x8048639 <welcome+48>: call 0x80484a0 <__isoc99_scanf@plt> 0x804863e <welcome+53>: mov eax,0x80487e3 0x8048643 <welcome+58>: lea edx,[ebp-0x70] 0x8048646 <welcome+61>: mov DWORD PTR [esp+0x4],edx 0x804864a <welcome+65>: mov DWORD PTR [esp],eax Guessed arguments: arg[0]: 0x80487dd ("%100s") arg[1]: 0xff9a2a58 --> 0xf7f5cd80 --> 0xfbad2a84 [------------------------------------stack-------------------------------------] 0000| 0xff9a2a40 --> 0x80487dd ("%100s") 0004| 0xff9a2a44 --> 0xff9a2a58 --> 0xf7f5cd80 --> 0xfbad2a84 0008| 0xff9a2a48 --> 0xff9a2ac8 --> 0xff9a2ae8 --> 0x0 0012| 0xff9a2a4c --> 0xf7df71bd (<_IO_new_do_write+29>: cmp ebx,eax) 0016| 0xff9a2a50 --> 0xf7f5cd80 --> 0xfbad2a84 0020| 0xff9a2a54 --> 0x80487f0 ("Toddler's Secure Login System 1.0 beta.") 0024| 0xff9a2a58 --> 0xf7f5cd80 --> 0xfbad2a84 0028| 0xff9a2a5c --> 0xf7df758b (<_IO_new_file_overflow+267>: add esp,0x10) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x08048639 in welcome () ``` 可以看到參數是用 stack 傳遞的,而且是從右往左 push 進 stack (雖然這裡沒有用 push 這個指令啦) <details> <summary><i style="color:blue">為什麼從右往左進 stack?</i></summary> 從右往左的原因是在那個函數裡面,只要 pop 出來就會是正確的順序,這是 x86 32bit 的做法 </details><br /> 所以我們知道這次 `name` 這個參數的位置就是 `0xff9a2a58` 為了知道這個值怎麼出現的,我們需要讀讀 `scanf` 的附近幾行 ```asm 0x0804862a <+33>: mov eax,0x80487dd 0x0804862f <+38>: lea edx,[ebp-0x70] 0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx 0x08048636 <+45>: mov DWORD PTR [esp],eax 0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt> ``` 你可以看到 `name` 的位置本來是放在 `edx` 裡面 因為 `mov DWORD PTR [esp+0x4],edx` 這個簡單換成 c 就是 `*(esp+4) = edx;` <details> <summary><i style="color:blue">為什麼 <code>esp+4</code> 是 <code>name</code> 而不是 <code>"%100s"</code>?</i></summary> 可以回去觀察上面的 stack ,你會發現 <code>"%100s"</code> 在 stack 的位置是 0xff9a2a40 ,比 <code>name</code>要低 <br /> 所以 <code>esp+4</code> 是 <code>name</code> 而 <code>esp</code> 是 <code>"%100s"</code> (或者也可以透過對 stack 的了解看出來) </details> <details> <summary><i style="color:blue">為什麼寫成 c 的時候 <code>DWORD PTR</code> 不見了?</i></summary> <code>DWORD PTR</code> 是為了指定 <code>mov</code> 要移動的大小是多大,<code>DWORD</code> 指定是 4 bytes <br /> 也就是說是把 <code>edx</code> 裡面的東西移進 <code>esp+0x4</code> ~ <code>esp+0x1</code> 的地方 </details><br /> 而 `edx` 裡面這個地址是哪來的呢? 這裡 `lea edx,[ebp-0x70]` `lea` (load effective address)的用法是 `lea reg, [addr]` (你說什麼是 effective address?抱歉我也搞不懂,我只會用, SADD) 總之就是把 [] 裡面的地址放到 register 裡面,所以 `name` 的地址放在 `edx` 裡面 + `edx` 裡面放的地址是 `ebp-0x70` = `name` 的地址是 `ebp-0x70`! 好耶! 然後我們就可以用一樣的手法看出 `passcode1` 跟 `passcode2` ㄌ 先讀一下 `login` (這裡用 disas ) ``` 0x08048564 <+0>: push ebp 0x08048565 <+1>: mov ebp,esp 0x08048567 <+3>: sub esp,0x28 0x0804856a <+6>: mov eax,0x8048770 0x0804856f <+11>: mov DWORD PTR [esp],eax 0x08048572 <+14>: call 0x8048420 <printf@plt> 0x08048577 <+19>: mov eax,0x8048783 0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10] 0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx 0x08048583 <+31>: mov DWORD PTR [esp],eax 0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt> 0x0804858b <+39>: mov eax,ds:0x804a02c 0x08048590 <+44>: mov DWORD PTR [esp],eax 0x08048593 <+47>: call 0x8048430 <fflush@plt> 0x08048598 <+52>: mov eax,0x8048786 0x0804859d <+57>: mov DWORD PTR [esp],eax 0x080485a0 <+60>: call 0x8048420 <printf@plt> 0x080485a5 <+65>: mov eax,0x8048783 0x080485aa <+70>: mov edx,DWORD PTR [ebp-0xc] 0x080485ad <+73>: mov DWORD PTR [esp+0x4],edx 0x080485b1 <+77>: mov DWORD PTR [esp],eax 0x080485b4 <+80>: call 0x80484a0 <__isoc99_scanf@plt> 0x080485b9 <+85>: mov DWORD PTR [esp],0x8048799 0x080485c0 <+92>: call 0x8048450 <puts@plt> 0x080485c5 <+97>: cmp DWORD PTR [ebp-0x10],0x528e6 0x080485cc <+104>: jne 0x80485f1 <login+141> 0x080485ce <+106>: cmp DWORD PTR [ebp-0xc],0xcc07c9 0x080485d5 <+113>: jne 0x80485f1 <login+141> 0x080485d7 <+115>: mov DWORD PTR [esp],0x80487a5 0x080485de <+122>: call 0x8048450 <puts@plt> 0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af 0x080485ea <+134>: call 0x8048460 <system@plt> 0x080485ef <+139>: leave 0x080485f0 <+140>: ret 0x080485f1 <+141>: mov DWORD PTR [esp],0x80487bd 0x080485f8 <+148>: call 0x8048450 <puts@plt> 0x080485fd <+153>: mov DWORD PTR [esp],0x0 0x08048604 <+160>: call 0x8048480 <exit@plt> ``` `scanf("%d", passcode1)` 的地方: ``` 0x08048577 <+19>: mov eax,0x8048783 0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10] 0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx 0x08048583 <+31>: mov DWORD PTR [esp],eax 0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt> ``` 可以看到這次第二個參數又是 `edx` 的值,而 `edx` 的值是 `ebp-0x10` 的值 `mov edx, DWORD PTR [ebp-0x10]` 相當於 `edx = *(ebp-0x10)` 所以可以知道 `passcode1` 的位置是 `ebp-0x10` (因為這裡第二個參數是 `passcode1` 的值而非地址) 再看 `passcode2` ``` 0x080485a5 <+65>: mov eax,0x8048783 0x080485aa <+70>: mov edx,DWORD PTR [ebp-0xc] 0x080485ad <+73>: mov DWORD PTR [esp+0x4],edx 0x080485b1 <+77>: mov DWORD PTR [esp],eax 0x080485b4 <+80>: call 0x80484a0 <__isoc99_scanf@plt> ``` 同上看出 `passcode2` 的位置是 `ebp-0xc` 所以我們現在三個變數的地址都有了 `name`: `ebp-0x70` `passcode1`: `ebp-0x10` `passcode2`: `ebp-0xc` 注意 `name` 是個長度 100 的陣列,所以我們可操控的範圍是 `ebp-0x70` ~ `ebp-0xd` 這時候 @t510599 做的 [stack 工具](https://app.stoneapp.tech/stack/)就可以派上用場啦(不用全部自己畫了,好耶!) 這個圖的最頂端是 `ebp` ![](https://i.imgur.com/jRwa4rZ.png) 可以發現只有 `passcode1` 能被安排,所以這個方法就 SADD 了 不過安排 `passcode1` 是有用的,在下篇會用到 [下篇傳送門](https://stoneapp.tech/cavern/post.php?pid=910)
2021-07-14 17:58:59
留言
Last fetch: --:-- 
現在還沒有留言!