MySQL Order By 注入的小技巧

0x00 前言 前几年在工作上遇到了一个 MySQL 的注入问题,注入点在 order by 后面,类似于 select 1 from dual order by "", *;` 其中, * 所在的地方即为注入点。 随即本人尝试对这个点进行注入,经过若干次尝试后均以失败告终。 具体尝试结果如下: 1. 报错注入 尝试使用常见的 GTID_SUBSET() 函数进行报错注入,发现 order by 后没有发挥应用的效果。 2. 延时注入 尝试使用的 sleep() 函数进行延时注入,发现 order by 后没有发挥应用的效果。 此时你可能会发现, order by 后面的行为稍显怪异,难以琢磨。 难道就没有办法利用这个注入了吗?肯定得有,不然这篇文章就写不下去了。 0x01 order by 后的注入 POC 在经过若干次尝试后,本人发现下面的查询语句能够触发报错。 select 1 from dual order by "",(SELECT(1)FROM(SELECT(GTID_SUBSET(2,2))where(1=1))test); # 报错 select 1 from dual order by "",(SELECT(1)FROM(SELECT(GTID_SUBSET(2,2))where(1=2))test); # 不报错 因此,完全可以利用上述语句进行 bool 类型的注入, 甚至直接一点,利用该特性进行报错注入: 或者将 GTID_SUBSET 换成 sleep 进行延时注入。 0x02 未完待续 本文能够利用一些特殊的 SQL,对 order by 后的注入点进行注入。然而,这个只是表现,并非原理。至于 MySQL 的行为为何如此奇怪,本人并没有了解清楚,有待研究。 ...

June 17, 2024 · 1 min · 84 words · SYM01

再谈 IP 伪造

0x00 前言 本篇文章最早由笔者发布在漕河泾小黑屋公众号上。然而,公众号始终不利于分享,故重新发表于博客上。 本篇文章主要介绍几种常见的伪造来源 IP 的方式。 0x01 方式1: X-Forwarded-For 这个是最为认知的 IP 伪造方法,早年的 CTF 题目也经常涉及,然而现在知道的人太多, CTF 都不屑于出这类题目。 X-Forwarded-For 诞生的原因比较简单粗暴。 对于一个非常简单的网络模型, 一个网络请求通常只有两方,即请求方与被请求方,如下所示。这样的网络模型下, Web Server 是可以拿到 User 的真实 IP 地址的,即使拿到的可能是路由器的地址。 User --> Web Server 但是上了规模的网站,其网络模型不会这么简单,它可能长这样: User --> CDN --> Web Server 在这种场景下, CDN 依旧可以拿到 User 的真实 IP 地址,然而 Web Server 却无法直接拿到。 为了解决这个问题, 有人提出了 X-Forwarded-For, 它作为 HTTP Header 传递给后端的 Web Server,其格式如下: X-Forwarded-For: 1.0.0.1, 2.0.0.2 细心的朋友可能会发现, 我是不是可以直接将 1.0.0.1 改成任意 IP 地址,然后直接将请求发送给 Web Server?没错,这就是非常简单的 X-Forwarded-For IP 伪造攻击。一般这类问题的解决思路是,校验 4 层协议的来源 IP,判断是否为可信 IP,比如是否为 CDN 的 IP。如果可信,才会尝试解析 X-Fowarded-For Header。 ...

June 17, 2024 · 3 min · 546 words · SYM01

利用 URN 绕过 URL 检查

0x00 前言 最近痴迷于看 RFC 及各类规范文档,从中发现一些有趣的利用。刚好前段时间发现了一处有趣的特性,成功绕过了某个知名站点的 XSS 防御,最终执行 XSS 攻击。本来以为只是一个小 trick,后来被某偶像拿来出了道 CTF 题目,觉得有必要分享一下。 0x01 一个真实 case 及其绕过 几个月前发现某网站上有类似于下面的逻辑,会从 URL 中取 next 参数的值,并解析出 pathname 部分,执行跳转。 const getParam = (key) => { return new URL(location).searchParams.get(key) } const nextURL = getParam('next') if (nextURL) { const u = new URL(nextURL) location.href = "" + u.pathname } 老司机应当可以发现,如果 u.pathname 能够以 javascript: 开头,那么就可以执行 XSS 攻击了。然而,pathname 会多带一个 ‘/’ ,导致利用失败,无法 XSS。 爱折腾的人怎么会止步于此呢,这个问题的突破点在于 new URL(M).pathname 。根据规范,如果cannot-be-a-base-URL 为 true,那么 pathname 等价于 path[0]。 刚好规范中就给出了满足这类条件的 case,如下图所示 因此,只要构造 ?next=urn:javascript:alert(location.origin) 即可绕过改限制并执行 XSS 攻击。 ...

August 22, 2021 · 1 min · 92 words · SYM01

利用 multipart boundary 绕过 WAF

0x00 前言 WAF(Web Application Firewall)是很常见的 Web 安全基础设施,许多云厂商、大厂、乙方安全公司均有相应的产品。然而,不得不承认,WAF 只能有限提升安全防护能力,不能拦截一些稍微复杂的攻击。正常业务不应当过度依赖 WAF,况且 WAF 还存在误拦截正常业务流量的可能。 目前已知的一些绕过 WAF 的手段包括但不限于: Chunked encoding 绕过 IBM037 等罕见编码绕过 多嘴一句:最早提出 IBM037 编码绕过 WAF 的应该是 Soroush Dalili 在 SteelCon 2017 上的议题,然而国内众多相关文章,基本没有标记出处,很奇怪。 笔者最近在分析 Go 语言的 HTTP 协议解析实现的时候,发现了一种能够利用 multipart boundary 绕过 WAF 的方法,在 Python 的一些 Web 框架上也适用,因而将其分享出来。 0x01 绕过 multipart/form-data 是一种非常常见的 HTML 表单编码方式,绝大部分的 Web 服务器、框架实现,均支持此编码。其编码后的请求大致如下所示,表单数据通过boundary分割。 POST /test HTTP/1.1 Host: example.com Content-Type: multipart/form-data; boundary=“boundary” —boundary Content-Disposition: form-data; name=“field1” value1 —boundary Content-Disposition: form-data; name=“field2”; filename=“example.txt” value2 —boundary— 那么只要满足上述协议要求,服务端就可以正常获取到字段内容了,如下图所示。 ...

August 2, 2021 · 1 min · 182 words · SYM01

iOS 中利用 Frida 解密任意 APP 的流量

0x00 前言 最近测试的APP里,越来越多的APP采用了加密流量的通信方式,即在原有的HTTP、HTTPS流量之上,又做了一层加密。 要对这些加密过程进行逆向,无疑要耗费大量的工作量,时间可能不允许。因此,可采用一种Hook的方式,将加密前/解密后的流量截获下来。 0x01 原理 通过加密流量与远端进行通信,势必会调用响应的加密、解密函数。因此,可通过Frida直接将未加密的流量Hook出来。 然而这样只能查看未加密的流量,不能篡改里边的数据,显然不太实用。因此,我们需要允许用户在流量进行加密/解密时,篡改里边的内容。 实现思路较为简单,如下所示: Hook加密函数,中断加密过程,将未加密的数据发送到本地搭建的一个服务器,并将HTTP代理设置为Burpsuite的代理 本地搭建的服务器原封不动地返回请求数据 Hook点收到服务器返回的数据后,利用服务器返回的数据替换原有数据,并恢复加密函数的执行过程 重复1、2、3的步骤,Hook掉解密函数 这样一来,Burpsuite就能够发挥其原有的作用,劫持未加密的流量,并对流量内容进行篡改了。 0x02 实现 以某APP为例,首先起一个echo服务器线程,专门负责原封不动地返回客户端的请求数据;其次用Frida hook掉相关加解密函数,将未加密的流量通过Burpsuite代理发往echo服务器。相关脚本内容如下: #!/usr/bin/env python3 # coding: utf-8 from time import sleep from threading import Thread from http.server import HTTPServer, BaseHTTPRequestHandler import sys import requests import frida ECHO_PORT = 28080 BURP_PORT = 8080 class RequestHandler(BaseHTTPRequestHandler): def do_REQUEST(self): content_length = int(self.headers.get('content-length', 0)) self.send_response(200) self.end_headers() self.wfile.write(self.rfile.read(content_length)) do_RESPONSE = do_REQUEST def echo_server_thread(): print('start echo server at port {}'.format(ECHO_PORT)) server = HTTPServer(('', ECHO_PORT), RequestHandler) server.serve_forever() # start echo server first t = Thread(target=echo_server_thread) t.daemon = True t.start() session = frida.get_usb_device().attach('平安普惠') script = session.create_script(''' var reqMethod = ObjC.classes.PHNetworkAgent['- requestOperationWithHTTPMethod:requestSerializer:URLString:parameters:']; var respDecrypt = ObjC.classes.PHSecurityHelper['+ phUnSecurityAESWithAesKey:content:']; var NSString = ObjC.classes.NSString; Interceptor.attach(reqMethod.implementation, { onEnter: function (args) { var methodStr = new ObjC.Object(args[2]).toString(); var urlStr = new ObjC.Object(args[4]).toString(); var param = new ObjC.Object(args[5]); var paramStr = param['- uxy_JSONString']().toString(); var data = { method: methodStr, url: urlStr, param: paramStr, }; send({type: 'REQ', data: data}) var op = recv('NEW_REQ', function(val) { var data = val.payload; args[2] = NSString.stringWithString_(data.method); args[4] = NSString.stringWithString_(data.url); args[5] = NSString.stringWithString_(data.param)['+ __uxy_JSONObject'](); }); op.wait(); } }); Interceptor.attach(respDecrypt.implementation, { onLeave: function (retval) { var resp = new ObjC.Object(retval); var data = resp.toString(); send({type: 'RESP', data: data}) var op = recv('NEW_RESP', function(val) { var data = val.payload; var newRetval = NSString.stringWithString_(data); retval.replace(newRetval); }); op.wait(); } }); ''') def on_message(message, data): if message['type'] == 'send': payload = message['payload'] _type, data = payload['type'], payload['data'] if _type == 'REQ': r = requests.request('REQUEST', 'http://127.0.0.1:{}/'.format(ECHO_PORT), proxies={'http': 'http://127.0.0.1:{}'.format(BURP_PORT)}, data=data['param'].encode('utf-8'), headers={ 'REQ_METHOD': data['method'], 'REQ_URL': data['url'], }) new_data = { 'method': r.headers.get('REQ_METHOD', data['method']), 'url': r.headers.get('REQ_URL', data['url']), 'param': r.text } script.post({'type': 'NEW_REQ', 'payload': new_data}) elif _type == 'RESP': r = requests.request('RESPONSE', 'http://127.0.0.1:{}/'.format(ECHO_PORT), proxies={'http': 'http://127.0.0.1:{}'.format(BURP_PORT)}, data=data.encode('utf-8')) script.post({'type': 'NEW_RESP', 'payload': r.text}) script.on('message', on_message) script.load() sys.stdin.read() 0x03 总结 套用上面的模板,可以快速地对加密流量的APP进行测试。然而实际应用上的难点在于找到相关加解密的函数,对于Objective-C这类没有明显调用关系的APP更甚。 ...

November 4, 2019 · 2 min · 282 words · SYM01