雲伺服器AWD平臺搭建
開學後實驗室來了幾個新同學,在線上CTF方面大家一直在持續學習,但AWD模式的CTF我們練習並不多,所以準備搭建一個AWD平臺用於實驗室成員的線下賽攻防練習。
最開始的是防災科技大學的線下AWD靶場:
https://github.com/glzjin/20190511_awd_docker
但是靶場沒有計分板和組隊顯示等功能,又找了一下:
https://github.com/zhl2008/awd-platform
騰訊雲伺服器沒有翻牆,從github上面拉取下來有495MB,本來想從本地電腦上下載後上傳到雲伺服器上使用 unzip 命令進行解壓,但 unzip 的速度也很慢,重新尋找解決辦法:
https://zhuanlan.zhihu.com/p/112697807
如果只是為了clone加速,完成到第四步就可以了:
我的碼雲拉取地址在:
https://gitee.com/Cl0udG0d/awd-platform
接著在雲伺服器上面使用 git clone
現在的速度就很快了:
花三十秒的時間下載完畢。
cd awd-platform/
目錄中有AWD線下環境手冊文件,但是在搭建的時候還是會有很多不完善的地方,綜合網上的多篇部落格共同搭建並優化。
AWD環境中的機器按照功能分為幾種型別:
-
Check_Server:
服務檢查伺服器,用於判定選手維護的服務是否可用,如果不可用,則會扣除相應的分數,不開啟任何埠,需要與flag伺服器通訊
簡單來說這臺機器的作用就是檢查靶機宕機沒有
-
Flag_Server:
選手提交flag的伺服器,並存儲選手的分數,開啟80埠
簡單來說這臺機器就是獲取到flag後的提交物件,用於加分
-
Web_Server:
選手連線的伺服器,選手需要對其進行維護,並嘗試攻擊其他隊伍的機器,通常開啟80埠,22埠,並將埠對映到主機。
這個就是我們每個隊伍所要操作的機器。
比賽邏輯拓補:
雲伺服器首先安裝docker,有很多師傅寫過安裝docker的文章,跳過這一步。
接著下載映象
docker pull zhl2008/web_14.04
接著按照參考文章裡面的:
所以我們將映象的名字修改:
docker tag zhl2008/web_14.04 web_14.04
接著我們按照文件裡面來進行操作:
後面的數字是靶機的數量,也就是參賽隊伍的數量,我們先複製所有隊伍的比賽資料夾和啟動比賽(這裡啟動的是2個參賽隊):
python batch.py web_yunnan_simple 2 python start.py ./ 2
這裡使用的靶機是 web_yunnan_simple ,至此,靶機就已經可以訪問了,因為是在一個伺服器上運行了多個docker,靶機的埠對映規則為:
team1 ---- 8801
team3 ---- 8802
team3 ---- 8803
....以此類推
如圖:
各個靶機的ssh密碼在目錄資料夾下的pass.txt檔案中,如圖
其ssh埠對映規則為:
team1 ---- 2201
team2 ---- 2202
team3 ---- 2203
....以此類推
但是我們還沒有啟動 check 指令碼,而專案中原來的check指令碼是不能用的,我們需要進行一些修改,這個規則要根據自己的環鏡自己編寫,總體思路就是判斷頁面是否存在,存在就加一分,不存在就減一分
網上有修改過後的 check 指令碼,同時可以看到 flag-server 和 check-server 所對映的埠
使用大佬們的check.py指令碼
#!/usr/bin/env python # -*- coding:utf8 -*- ''' ''' import hashlib import base64 sleep_time = 300 debug = True headers = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"} import time import httplib import urllib2 import ssl my_time = 'AAAA' __doc__ = 'http(method,host,port,url,data,headers)' flag_server = '172.17.0.1' key = '744def038f39652db118a68ab34895dc' hosts = open('host.lists','r').readlines() user_id = [host.split(':')[0] for host in hosts] hosts = [host.split(':')[1] for host in hosts] port = 80 def http(method,host,port,url,data,headers): con=httplib.HTTPConnection(host,port,timeout=2) if method=='post' or method=='POST': headers['Content-Length']=len(data) headers['Content-Type']='application/x-www-form-urlencoded' con.request("POST",url,data,headers=headers) else: headers['Content-Length'] = 0 con.request("GET",url,headers=headers) res = con.getresponse() if res.getheader('set-cookie'): #headers['Cookie'] = res.getheader('set-cookie') pass if res.getheader('Location'): print "Your 302 direct is: "+res.getheader('Location') a = res.read() con.close() return a def https(method,host,port,url,data,headers): url = 'https://' + host + ":" + str(port) + url req = urllib2.Request(url,data,headers) response = urllib2.urlopen(req) return response.read() def get_score(): res = http('get',flag_server,8080,'/score.php?key=%s'%key,'',headers) print res user_scores = res.split('|') print "******************************************************************" res = '' print res print "******************************************************************" return user_scores def write_score(scores): scores = '|'.join(scores) res = http('get',flag_server,8080,'/score.php?key=%s&write=1&score=%s'%(key,scores),'',headers) if res == "success": return True else: print res raise ValueError class check(): def index_check(self): res = http('get',host,port,'/index.php?file=%s'%str(my_time),'',headers) if 'perspi' in res: return True if debug: print "[fail!] index_fail" return False def server_check(): try: a = check() if not a.index_check(): return False return True except Exception,e: print e return False game_round = 0 while True: scores = get_score() scores = [] print "--------------------------- round %d -------------------------------"%game_round for host in hosts: print "---------------------------------------------------------------" host = host[:-1] if server_check(): print "Host: "+host+" seems ok" scores.append("0") else: print "Host: "+host+" seems down" scores.append("-10") game_round += 1 write_score(scores) time.sleep(sleep_time)
按照文件啟動check服務
docker attach check_server/
python check.py
使用的騰訊雲伺服器,這一步的時候出錯了
連線超時,檢視 check.py 原始碼
game_round = 0 while True: scores = get_score() scores = [] print "--------------------------- round %d -------------------------------"%game_round for host in hosts: print "---------------------------------------------------------------" host = host[:-1] if server_check(): print "Host: "+host+" seems ok" scores.append("0") else: print "Host: "+host+" seems down" scores.append("-10") game_round += 1 write_score(scores) time.sleep(sleep_time)
執行的是這一段
while迴圈呼叫,一段時間延遲後進行服務可用性的檢查,延遲時間由sleep_time決定
get_score() 函式報錯,檢視報錯行:
res = http('get',flag_server,8080,'/score.php?key=%s'%key,'',headers)
呼叫了自寫頁面請求函式 http
而 flag_server 為全域性變數:
my_time = 'AAAA' __doc__ = 'http(method,host,port,url,data,headers)' flag_server = '172.17.0.1' key = '744def038f39652db118a68ab34895dc' hosts = open('host.lists','r').readlines() user_id = [host.split(':')[0] for host in hosts] hosts = [host.split(':')[1] for host in hosts] port = 80
為:
flag_server = '172.17.0.1'
其靶機IP在host.lists檔案中,ssh連結,檢視其中一臺靶機的IP
可以看到雲伺服器上靶機的內網IP實際上為 172.18.0.2,所以才會報錯超時。
修改 flag_server
修改host.lists檔案
重新啟動並進入容器:
可以看到現在 check已經正常了,但是host.lists檔案是自動生成的,為了避免每次都修改,我們需要修改其自動化生成的指令碼
簡單尋找了一下,初始化檔案在start.py裡面
如圖,host.lists檔案寫入的IP根據我們的情況修改為 172.18.0
使用
python stop_clean.py
命令清理環境重新啟動服務,檢視是否正常啟動
python batch.py web_yunnan_simple 3//複製3個web_yunnan_simple的靶機,數值可改 python start.py ./ 3 //啟動三個docker靶機和check伺服器、flag_server伺服器。數值可改 docker attach check_server //連結裁判機,檢查是否正常 python check.py
現在check裁判機就正常了
此外需要注意的是:
檢測頁面是否存活是在check.py中的 check類中,對於不同的環境,我們需要編寫不同的類方法來進行檢測服務是否宕機
該AWD平臺另一個問題是可以無限交flag,即一個flag可以無限刷分,詳情和修改方法參考:
https://www.cnblogs.com/Triangle-security/p/11332223.html#
個人感覺修改方法不是很優雅hhh,因為需要自己提前去執行該指令碼,這段時間有空想想其他的解決辦法,指令碼如下:
#!/usr/bin/env python #coding:UTF-8 import time import os print int(time.time()) Unix_time = int(time.time()) print time.ctime(Unix_time) while True: time_his = [] time_list = ["00","05","10","15","20","25","30"] for i in time_list: dt = "2019-04-28 10:"+str(i)+":00" time_his.append(dt) a = time_his[0] b = time_his[1] c = time_his[2] d = time_his[3] e = time_his[4] f = time_his[5] g = time_his[6] time_stamp = [a,b,c,d,e,f,g] for k in time_stamp: h = open("time.txt", 'w+') timeArray = time.strptime(k, "%Y-%m-%d %H:%M:%S") timestamp = time.mktime(timeArray) print (int(timestamp)) data = (int(timestamp)) separated = '|' zero = '0' print >>h,(zero),(separated),(data),(separated),(zero),(separated),(data),(separated),(zero),(separated),(zero),(separated),(data),(separated),(zero),(separated),(zero), # 0|data|0|data|0|0|data|0|0 h.close() time.sleep(300)
另外,計分板在 IP:8080/score.txt中,介面不是很好看
使用夜莫離大佬的介面修改之
https://pan.baidu.com/s/18KlIeluaTtm-kT3KuXHseQ
密碼:cvdn
修改過程為:
計分板檔案拷貝至awd-platform下的flag_server資料夾下。要注意將檔案score.txt與result.txt檔案許可權調至777,這樣才能刷新出分值。
另外部分部落格說的是修改 scorecard.php檔案,下載上面的百度網盤檔案後,發現其檔案內容為:
應該是夜莫離後面又將scorecard.php改為了index.php,所以我們修改index.php中的IP地址
想要檢視各隊得分情況直接訪問 IP:8080即可
可以看到因為只有三個隊伍,所以這裡只有前三個隊伍有分數,為0,其餘三個隊伍是沒有分數的,不知道如果開啟了超過六個隊伍,分數板會變成什麼樣子。
至此AWD平臺安裝完成。
該AWD平臺題目環境較多,雖然安裝的時候問題比較多,但都是能夠克服的,儘管有無限刷分的bug,但是瑕不掩瑜,應該是開源AWD平臺中最好的一個(很多沒有bug的平臺,題目環境又太少了)。
自己也想寫一個AWD平臺hhh,希望能夠自帶十道以上題目環境,一鍵部署,同時少量bug不影響正常使用,支援NPC隊伍,以及有程式碼功底的使用者,能夠自己快速新增CMS題目環境進來,擴充套件題目種類。這樣就能夠方便很多因為各種原因不能進入線下的學校來進行AWD的練習了。
做技術的hack心態加上開放共進的態度是成長和越過高山幽谷的祕籍
以上
參考連結:
https://www.cnblogs.com/Triangle-security/p/11332223.html#
https://www.heibai.org/post/1468.html