确实是高质量比赛,学到了很多知识、认识到了很多的不足。

任重而道远啊…

hgame_week1webClassic Childhood Game

F12检查源码,打开Events.js

发现

\x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x73\x53\x31\x6c\x59\x57\x6d\x68\x6a\x4d\x6b\x35\x35\x59\x56\x68\x43\x4d\x45\x70\x72\x57\x6a\x46\x69\x62\x54\x55\x31\x56\x46\x52\x43\x4d\x46\x6c\x56\x59\x7a\x42\x69\x56\x31\x59\x35

cyberchef解码

Show Me Your Beauty

文件上传

在一张图片内写入

上传用burpsuite抓包

修改文件名为pHp

上传成功

回到网页,右键头像打开,测试phpinfo();

)

成功,连接蚁剑,根目录下找到flag:hgame{Unsave_F1L5_SYS7em_UPL0ad!}

Become A Member

(赛后复现) 比赛时改错头了一直没回显…直接尬住

考点就是 ⼏个常规的HTTP考点+请求数据构造

题⽬提⽰⾝份证明(Cute-Bunny),尝试 User-Agent

Vidar的邀请码(code),可以看⼀开始的http请求头当中有⼀个 Set-Cookie:code=guest ,将 guest 修改为 Vidar 即可

来⾃ bunnybunnybunny.com 的申请,提⽰了请求来源,将 referer 的值修改为bunnybunnybunny.com

最后⼀个是本地请求,这个⽐较常⻅,添加 X-Forwarded-For: 127.0.0.1 的请求头即可

然后给了 password 和 username ,按正常的json数据格式发请求数据即可(注意需要指定Content-Type 为 application/json ,请求⽅式是 GET )

)

得到flag:hgame{H0w_ArE_Y0u_T0day?}

Guess Who I Am

(赛后复现) 这是一道爬虫题,写脚本这一块还得不断加强才行啊..

F12看到hint,去GitHub把成员信息扒下来,写成json文件,改成这种形式

⽐较快捷的办法的话⽐如说⽤像vsode之类的编辑器来做批量的编辑。可以ctrl + f选中所有需要编辑的信息后,右键更改所有匹配项,让每⼀个 “avatar” 前⾯都出现光标,再⽤shift + ⽅向键,就可以⽤多个光标批量选中删除啦。

下一步我们要做的内容就是读⼊json⽂件并且解析内容,请求获取成员介绍,通过json⾥读进来的内容匹配出对应的ID,⽤这个ID去请求验证答案的接⼝,当得分超过100时去关注返回的⻚⾯看看有没有什么不⼀样的信息。

步骤一:

读⼊json⽂件并且解析内容

import jsonwith open('member.json',encoding='UTF-8') as f:    member = json.load(f)print(member)

这里要加encoding='UTF-8',不然可能会出现UnicodeDecodeError: 'gbk' codec can't decode byte 0xa7 in position 58: illegal multibyte sequence 的编码问题

json.load():json文件中读取数据

步骤二:

看到正常输出了,接下来F12看⼀下⽹站的后端接⼝设计,请求对应的后端接⼝拿到所需的各种信息

提问信息的地址:http://week-1.hgame.lwsec.cn:32230/api/getQuestion

分数的地址:http://week-1.hgame.lwsec.cn:32230/api/getScore

答题的地址:http://week-1.hgame.lwsec.cn:32230/api/verifyAnswer此处是POST

这⾥可能就会考虑⼀个问题,⽹站是怎么对于不同⽤⼾的信息进⾏判断的呢,这⾥也没有什么登陆啊,我能不能直接让服务端认为我已经拿到了100分呢?站点通过Cookie中的session来记录了⽤⼾信息,⽽session是通过Key来加密的,所以不知道Key的⽤⼾是没有办法做到在客⼾端修改分数的。

步骤三:

我们这⾥要带着session去请求 /api/getQuestion 的接⼝来获取问题。

这⾥我们获得了⼀个json格式的数据,我们的⽬标是去提取我们想要的成员介绍的内容,但是请求得到的数据类型是str,所以需要转换成Dict。

所以加上这两句

rs = json.loads(res.text)print(rs['message'])

得到

详解:

cookies:初始化cookies字典变量
res=requests.get(“url”,cookies=cookies)将cookies添加到get方法中,获取到的res.content中就是我们将cookies信息添加到get中后访问网页所获取的内容
json.loads():将str类型的数据转换为dict类型
res.text查看响应内容,response.text 返回的是Unicode格式的数据

步骤四:

获取到对应的成员介绍后我们就在成员信息中匹配到对应的ID

answer = ''for i in member:    if i['intro'] == rs['message']:        answer = i['id']        breakprint(answer)

验证答案

答案正确,可以进行下一步的脚本编写

由于后端⽤的语⾔是Golang,开发框架是Gin,鉴权⽤的session中间件是客⼾端session的形式,也就是说包括分数包括问题都是存储在session中,那么我们每次答对题⽬分数和问题都是会改变的,在⻚⾯上我们答对问题会有 set-cookie 的响应头来重新设置新的session,但是我们编写脚本就得⾃⼰来处理这个问题,需要从set-cookie中拿到新的session然后再开始新⼀轮的⾃动答题

步骤五:

获取页面的session

    data = {'id':answer}    res = requests.post(url+"/api/verifyAnswer",cookies=cookies,data=data)    rs=json.loads(res.text)    print("[*] Result: "+ rs['message'])    cookies['session'] = res.cookies['session']

步骤六:

补上对于分数接⼝的请求,外层加上⼀个While循环,判断分数到达100时退出,因为后端接⼝总共

也就只有获取问题,获取分数,验证答案三个,那么我们可以判断分数到达100时肯定是从这三个接⼝会能够返回和Flag相关的信息

完整版EXP:

import jsonimport requests# 读取json⽂件并且解析with open('member.json', encoding='UTF-8') as f:    member = json.load(f)cookies={}answer=''score=0# 靶机地址url="http://week-1.hgame.lwsec.cn:31628"#注意此处不要多复制‘/’,会造成‘//’现象直接报错# 当前页面的sessioncookies['session']='MTY3NDIxMjQ0OHxEdi1CQkFFQ180SUFBUkFCRUFBQU9fLUNBQUlHYzNSeWFXNW5EQTBBQzJOb1lXeHNaVzVuWlVsa0EybHVkQVFDQUF3R2MzUnlhVzVuREFnQUJuTnZiSFpsWkFOcGJuUUVBZ0FzfLcGFI3K5NeFt3s0VGWxGso8KEtNu96yx9qX70XNWqjK'while score <100:    # 请求getQuestion接⼝    res=requests.get(url+"/api/getQuestion",cookies=cookies)    rs = json.loads(res.text)    print('[+] getQuestion:'+res.text)    # 匹配对应的ID    for i in member:        if i['intro'] == rs['message']:            answer = i['id']            break            # 请求verifyAnswer接⼝    data = {'id':answer}    res = requests.post(url+"/api/verifyAnswer",cookies=cookies,data=data)    rs=json.loads(res.text)    print("[*] Result: "+ rs['message'])    cookies['session'] = res.cookies['session']    # 请求getScore接⼝    res=requests.get(url+"/api/getScore",cookies=cookies)    rs=json.loads(res.text)    score=rs['message']    print('[+] Score:'+str(rs['message']))

实现效果

得到flag:hgame{Guess_who_i_am^Happy_Crawler}

cryptoRSA

在线网站分解n得到p,q http://factordb.com/

p=11239134987804993586763559028187245057652550219515201768644770733869088185320740938450178816138394844329723311433549899499795775655921261664087997097294813q=12022912661420941592569751731802639375088427463430162252113082619617837010913002515450223656942836378041122163833359097910935638423464006252814266959128953

脚本解题

import gmpy2from Crypto.Util.number import *p=11239134987804993586763559028187245057652550219515201768644770733869088185320740938450178816138394844329723311433549899499795775655921261664087997097294813q=12022912661420941592569751731802639375088427463430162252113082619617837010913002515450223656942836378041122163833359097910935638423464006252814266959128953e = 65537c=110674792674017748243232351185896019660434718342001686906527789876264976328686134101972125493938434992787002915562500475480693297360867681000092725583284616353543422388489208114545007138606543678040798651836027433383282177081034151589935024292017207209056829250152219183518400364871109559825679273502274955582n=135127138348299757374196447062640858416920350098320099993115949719051354213545596643216739555453946196078110834726375475981791223069451364024181952818056802089567064926510294124594174478123216516600368334763849206942942824711531334239106807454086389211139153023662266125937481669520771879355089997671125020789phi=(p-1)*(q-1)d=gmpy2.invert(e,phi)m=gmpy2.powmod(c,d,n)print(long_to_bytes(m))

得到flag:hgame{factordb.com_is_strong!}

神秘的电话

(赛后复现)…卡最后一步上了,属实没想到北欧神话的终点是诸神黄昏的幸存神vidar…

诸神黄昏的各种简写都试了结果是这个…还是不够细啊

第一步 将encrypted_message.txt中的内容base64解码

得到

⼏个星期前,我们收到⼀个神秘的消息。但是这个消息被重重加密,我们不知道它的真正含义是什

么。唯⼀知道的信息是关于密钥的:“只有倒着翻过⼗⼋层的篱笆才能抵达北欧神话的终点”。

第二步,手翻morse.wav的摩斯电码,因为flag中字母均为小写

得到0223e_priibly__honwa_jmgh_fgkcqaoqtmfr

第三步,逆序

第四步,18层栅栏解码

第五步,维吉尼亚解码,得到flag:hgame{welcome_to_hgame2023_and_enjoy_hacking}

miscSign In

签到题,base64得flag:hgame{Welcome_To_HGAME2023!}

e99p1ant_want_girlfriend

修改图片高度得到flag:hgame{e99p1ant_want_a_girlfriend_qq_524306184}

神秘的海报

Stegsolve打开,lsb 000 看到

Sure enough, you still remember what we talked about at that time! This is part of the secret: hgame{U_Kn0w_LSB&W I put the rest of the content here, https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEqBxm7RUk-G/view?usp=sharing, if you directly access the google drive cloud disk download in China, it will be very slow, you can try to use Scientific Internet access solves the problem of slow or inaccessible access to external network resources. This is my favorite music, there is another part of the secret in the music, I use Steghide to encrypt, the password is also the 6-digit password we agreed at the time, even if someone else finds out here, it should not be so easy to crack (( hope so

前半段flag跟wav文件下载链接,结合后文提示steghide隐写,密码是6位纯数字,用stegseek爆破

superdic.txt为6位纯数字的密码本

得到后半段:恭喜你解到这里,剩下的Flag是 av^Mp3_Stego},我们Week2见!

flag:hgame{U_Kn0w_LSB&Wav^Mp3_Stego}

Where am I

foremost分解出流量包里的压缩包,是个rar,一打开就是各种报错,缺少图片的文件头,密码以及不可预料的压缩末端。尝试用winrar修复但是不行,研究了一下,用NetworkMiner这个工具分离出的rar是有文件尾的,也防止自己手动修复时出错。

研究半天修复文件头,结果发现是rar伪加密

将24改为20即可

打开图片,在备注处看到gps信息,用https://exif.tuchong.com/ EXIF查看器更方便

得到信息:39 deg 54' 54.18" N, 116 deg 24' 14.88" E,0 m Above Sea Level

得到flag:hgame{116_24_1488_E_39_54_5418_N}

reversetest your IDA

放入IDA一打开就能看到flag

easyasm

观察发现是需要将result异或0x33后转字符串

脚本:

s='0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e's=s.split(',')l=[]for i in s:   l=int(i,16)^0x33   print(l,end='')s=[104,103,97,109,101,123,119,101,108,99,48,109,101,95,116,48,95,114,101,95,119,111,114,49,100,33,125]for i in s:    i=chr(i)    print(i,end='')

得到flag:hgame{welc0me_t0_re_wor1d!}

pwntest_nc

kali 中 nc 后 cat flag

IOTHelp marvin

(赛后复现)第一次做IOT的题,按照wp复现成功,刚好记录一下

给了hint知道本题为SPI协议解析 使⽤pulesview打开sr⽂件

SPI协议特性

SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线。

SPI总线包括4条逻辑线,定义如下:

MISO:Master input slave output 主设备数据输入,从设备数据输出(数据来自从机);

MOSI:Master output slave input 主设备数据输出,从设备数据输入(数据来自主机);

SCLK :Serial Clock 串行时钟信号,由主机产生发送给从机;

NSS:从机片选使能信号线。该信号由主机进行控制。在一主对多从的模式下,每一个从机都需要一个NSS,用于主机选择和那个从机进行通信(一般为低电平有效)。当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备。NSS也可以是CE,CS或SSEL

三根线上有波形 且有根频率最⾼ 应该是4线或以上的带时钟的通信协议

D0是时钟信号 ( CLK )

D1是片段CS

D2是数据(MISO)

如图设置,即可看的SPI的数据

看到7D,直接能联系到ascii码值为},故解码得flag

s='34,67,61,6d,65,7b,34,5f,35,74,34,6e,67,65,5f,53,70,31,7d's=s.split(',')flag=''for i in range(len(s)):    flag+=chr(int(s[i],16))print(flag)

得到flag:4game{4_5t4nge_Sp1}

hgame_week2webGit Leakage

读题目得知是git泄露

访问url/.git

下载index,打开搜索flag看到Th1s_1s-flag

去下载url/Th1s_1s-flag得到flag:hgame{Don't^put*Git-in_web_directory}

v2board

百度到V2Board Admin.php 越权访问漏洞

GitHub搜索exp,找到了zgao264师傅写的exp

按照README.md操作,得到token

cryptorabin

Rabin算法是一种基于模平方和模平方根的非对称加密算法

import gmpy2import libnump=65428327184555679690730137432886407240184329534772421373193521144693375074983q=98570810268705084987524975482323456006480531917292601799256241458681800554123c=4086661358212073245252744496322167481491672871949606958127237667510352936336492238168574196919178461270299415887662858793221972137767350873928701793072470n=6449323225107597053933443750923454260964062647115639999185223478236611437289957562214567685539436238408656830242287962245944636381136963632264486074804909e = 2inv_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) % nb = n - int(a)c = (inv_p * p * mq - inv_q * q * mp) % nd = 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

这道题出的真挺不错的

给了四个挑战,打通关获得flag

第一关是最基础的rsa,通过给的p值与p*q*r值得到q*r的值,值这么小直接yafu分解,得到q值和r值

这样p,q,e,c齐全,直接解,得到第一关答案:m<n_But_also_m<p

第二关是利用欧几里得算法,运行两次得到两个n值,其中的p值是相等的

import gmpy2p=gmpy2.gcd(n1,n2)q=n1//p

这样就得到了p,q的值,现在p,q,e,c齐全,得到第二关答案:make_all_modulus_independent

第三关是小e,明显是低加密指数攻击

脚本解题

import libnumimport gmpy2c=1620135819727459374856861031843595560446547666362278256721611098300571774064092226213485034156592394325852384589395122643937335495320149363516518672591127083237362181645206738138892454081130678965267391894846777695757814984112690455549768e=3n=89205429359119794945983435440440153891896048605500448398466813204176930432316502363982420399189336214133892667017070960272307319029776828466846047959788316617559138703334632404710945248239429508995159045446455803886466666986158213938827010650078577668654313324766202011427074264152957817749714057009436177683def exp(n, e, c):    k = 0    while 1:        m1 = k * n + c        m, t = gmpy2.iroot(m1, e)        if t:            print(m)            print(k)            print(libnum.n2s(int(m)))            break        k += 1exp(n, e, c)

得到第三关答案:encrypt_exponent_should_be_bigger

第四关的两次e值不同,得知是共模攻击

脚本解题

import libnumimport gmpy2n=66235966847778258236086556078519489918007499753458367888673525245830804410711231915934431942266308073011531618067138951010571129793107908204734598775186910049124825904093866926551514996215164837557886573947078401045910146184667712652713463353890419328910304579191100714494895346655422228997743287867163201599e1=105499e2=91183c1=6114296529012724857259078691024991747448962852327827400348864045083302424580717261899703658605007866073901365881990655212941056350327573395899227984885284491471483713972704047186525253301027249738411890418148826824912176701147082190936101112270993407013405680182248174829038778266977210861721974168651553374c2=0x509f556d9b19a1257fc91c284690a8e6e35e36bf1993c7b0ff21173ae015dbfcfdc0f9be257dd2400db11360f49ffb86fa3ae1f4b9f4abff43bac70f612bedb83c7b7eb5f32ebfb485077963195483ddd7df6f7e55c85bdccba88452b2546c929b68dca67df76ab0f158c383d0e187ed3bb75eb84cd4d5ac71c5b3f9d3564b46def exp_def(e1,e2,c1,c2,n):    s,s1,s2 = gmpy2.gcdext(e1, e2)    m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n    return int(m)    m=exp_def(e1,e2,c1,c2,n)print(libnum.n2s(m))

得到第四关答案:never_uese_same_modulus

得到最终flag:hgame{W0w_you^knowT^e_CoMm0n_&t$ack_@bout|RSA}

miscSign In Pro Max

flag分为5个部分,拼成uuid格式

part1为base套娃,base64、base58、base32得f51d3a18

part2、3 为md5得f91c4952

part4为sha256得a3ed

part5为ascii码位移得0bc0ea61d21c

s='0gh0jf61i21h'flag=''for i in s:    if ord(i)>=97:        data=chr(ord(i)-5)        flag+=data    else:        flag+=iprint(flag)

拼接flag:hgame{f51d3a18-f91c-4952-a3ed-0bc0ea61d21c}

crazy_qrcode

(赛后复现)姿势错了orz

在https://merricx.github.io/qrazybox/中导入该二维码,在tools list 选择 Brute-force Format Info Pattern

然后将模式转为Decode Mode,点击Decode即可获得密码:

解开压缩包,发现是25张二维码碎片,附加文件是一个数组,猜测是每一个二维码碎片的旋转次数

尝试拼出二维码(用ppt拼的,确实好用)

扫描得到flag:Cr42y_qrc0de