2024届龙信杯电子数据取证题解-流量分析部分

文摘   2024-10-28 21:48   江苏  

        上一篇题解部分答案有问题,本篇文章已经更改,感谢大佬的指正。更改后在结尾加了一个解密的脚本。

        题目很综合,设计的知识点很好:基本就是渗透的过程,流量的总体内容上就是先通过目录扫描,找到了wp的文件上传位置,然后利用对图片没有过滤的漏洞上传webshell图片马,上传图片马之后又上传了一个连接用的php的webshell,最后进行信息资产收集,对用户信息、ip信息、网站的配置文件,用sqlmap爆破登录网页,最后得到了主机密码,成功完成提权操作。(比赛建议:个人赛一定不要先做流量,时间长还不容易得分)

1.分析流量包检材,给出管理员对web环境进行管理的工具名。(标准格式:小皮)

宝塔

在流量开头的DNS里面,有写到连接了宝塔服务的站点

之前写的是wordpress,但是和web环境进行管理相比可能宝塔更接近

2.分析流量包检材,给出攻击者的ip地址是多少。(标准格式:127.0.0.)

192.168.209.135

这里可以看出来是135给147发的包(包括后面各种渗透的操作),所以是192.168.209.135是攻击者

3.分析流量包检材,给出攻击者爆破出的网站非管理员用户名是。(标准格式:admin)

saber

这个地方有点小坑,一共爆破出的用户名有两个是密码错误,而用户名正确的。但是最后攻击者是使用saber用户登陆的

用下面脚本能运行出来有两个账号是存在,但是密码错误

import os

def find_files_without_string(target_string):
  for root, dirs, files in os.walk('.'):
      for file in files:
          if file.endswith(".html") and "wp-login.php[" in file:
              file_path = os.path.join(root, file)
              with open(file_path, 'r', encoding='utf-8') as f:
                  content = f.read()
                  if target_string not in content:
                      print(file_path)

# 使用示例
target_string = '未在本站点注册'
find_files_without_string(target_string)

1056之后都是空的,不用看,重点查看3、11、568、824

saber用户的

luna用户的

后面有一个用saber这个账号cookie登陆的

这个分析后面的流量,可以看到抓取过用户的信息(在116流里面)

4.分析流量包检材,攻击者进行目录扫描得到的具有后门的页面url路径为。(标准格式:/abc.html)

/up_load.html

扫描目录之后,在up_load.html上面上传了webshell

5.分析流量包检材,攻击者通过修改请求包中的哪个字段导致恶意文件成功上传。(标准格式:test-type)

Content-Type

题目问的是修改什么导致成功上传的,根据渗透的经验来看,一般利用文件上传都是限制文件类型格式,所以这里修改了文件类型,上传了一个图片马

然后成功上传

6.分析流量包检材,攻击者上传成功的恶意文件, 该文件的临时存放路径是。(标准格式:/abc/edf)

/tmp/php38mbeJ

这里先上传了一个图片马

然后在后面又上传了一个phpwebshell脚本,所以我认为恶意文件应该是指下面的phpwebshell


从这里开始是冰蝎3流量解密,通过前面图片马和上传的phpwebshell可以看出来是冰蝎3

小技巧冰蝎3的经典开头3Mn1y:

然后分析流量时,建议先分析响应体再分析访问体。然后冰蝎流量解密用的是默认密钥e45e329feb5d925b,前面流量内容能看到。然后用aes解密——>base64就可以了。

次base64解密

7.分析流量包检材,服务器php配置文件的存放位置(标准格式:/www/sev/php.ini)

/www/server/php/82/etc/php.ini

后面有一段流量获取了访问页面


8.分析流量包检材,被攻击的web环境其数据库密码是。(标准格式:qwer1234)

X847Z3QzF1a6MHjR

在流127里面紧接着就抓取了配置文件

分析响应流量得到密码

9.分析流量包检材,服务器管理存放临时登录密码的位置。(标准格式:/tmp/pass)

/tmp/tmppass

这里有个小技巧,一般流量的抓取都是到抓到渗透到主机为止,所以这里从后往前分析会快一点

在最后一段加密流量里面


10.分析流量包检材,黑客获取的高权限主机的登录密码。(标准格式:qwer1234)

passwd!@#

最后一个加密流量就是

流量文件提取脚本

import subprocessimport os
def extract_http_traffic(pcap_file, output_dir): """ 提取 HTTP 请求和响应内容,并将 HTTP 对象(如文件)导出到指定目录。 :param pcap_file: 要分析的 .pcap 文件路径 :param output_dir: 导出 HTTP 文件的目录 """ # 确保输出目录存在 if not os.path.exists(output_dir):        os.makedirs(output_dir) try: # 提取 HTTP 请求和响应的详细信息 print("[*] 提取 HTTP 请求和响应...") http_requests = subprocess.check_output( ['tshark', '-r', pcap_file, '-Y', 'http.request or http.response', '-T', 'fields', '-e', 'http.host', '-e', 'http.request.uri', '-e', 'http.file_data'], text=True        ) # 输出 HTTP 请求和响应的摘要 print("[*] HTTP 请求和响应内容:\n")        print(http_requests) # 导出 HTTP 中的文件对象 print("[*] 导出 HTTP 对象到目录:", output_dir)        subprocess.check_call(['tshark''-r', pcap_file, '--export-objects'f'http,{output_dir}'])        print(f"[*] HTTP 对象已导出到 {output_dir} 目录。") except subprocess.CalledProcessError as e: print(f"[!] Tshark 执行错误: {e}") except Exception as e:        print(f"[!] 出现错误: {e}")if __name__ == "__main__": # 替换为你的 .pcap 文件路径,注意文件类型 pcap_file = ' ' # 替换为你想导出 HTTP 对象的目录 output_dir = ' ' extract_http_traffic(pcap_file, output_dir)

然后把下面的脚本扔到提取出来文件的文件夹里面

冰蝎流量解密脚本(脚本临时写的,能用就行)

from Crypto.Cipher import AESimport base64import binasciiimport jsonimport osimport re# 数据类class MData():    def __init__(self, data = b"",characterSet='utf-8'):        # data肯定为bytes        self.data = data        self.characterSet = characterSet      def saveData(self,FileName):        with open(FileName,'wb') as f:            f.write(self.data)
def fromString(self,data): self.data = data.encode(self.characterSet) return self.data
def fromBase64(self,data): self.data = base64.b64decode(data.encode(self.characterSet)) return self.data
def fromHexStr(self,data): self.data = binascii.a2b_hex(data) return self.data
def toString(self): return self.data.decode(self.characterSet)
def toBase64(self): return base64.b64encode(self.data).decode()
def toHexStr(self): return binascii.b2a_hex(self.data).decode()
def toBytes(self): return self.data
def __str__(self): try: return self.toString() except Exception: return self.toBase64()

### 封装类class AEScryptor(): def __init__(self,key,mode,iv = '',paddingMode= "NoPadding",characterSet ="utf-8"): ''' 构建一个AES对象 key: 秘钥,字节型数据 mode: 使用模式,只提供两种,AES.MODE_CBC, AES.MODE_ECB iv:iv偏移量,字节型数据 paddingMode: 填充模式,默认为NoPadding, 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding characterSet: 字符集编码 ''' self.key = key self.mode = mode self.iv = iv self.characterSet = characterSet self.paddingMode = paddingMode self.data = ""
def __ZeroPadding(self,data): data += b'\x00' while len(data) % 16 != 0: data += b'\x00' return data
def __StripZeroPadding(self,data): data = data[:-1] while len(data) % 16 != 0: data = data.rstrip(b'\x00') if data[-1] != b"\x00": break return data
def __PKCS5_7Padding(self,data): needSize = 16-len(data) % 16 if needSize == 0: needSize = 16 return data + needSize.to_bytes(1,'little')*needSize
def __StripPKCS5_7Padding(self,data): paddingSize = data[-1] return data.rstrip(paddingSize.to_bytes(1,'little'))
def __paddingData(self,data): if self.paddingMode == "NoPadding": if len(data) % 16 == 0: return data else: return self.__ZeroPadding(data) elif self.paddingMode == "ZeroPadding": return self.__ZeroPadding(data) elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding": return self.__PKCS5_7Padding(data) else: print("不支持Padding")
def __stripPaddingData(self,data): if self.paddingMode == "NoPadding": return self.__StripZeroPadding(data) elif self.paddingMode == "ZeroPadding": return self.__StripZeroPadding(data)
elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding": return self.__StripPKCS5_7Padding(data) else: print("不支持Padding")
def setCharacterSet(self,characterSet): ''' 设置字符集编码 characterSet: 字符集编码 ''' self.characterSet = characterSet
def setPaddingMode(self,mode): ''' 设置填充模式 mode: 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding ''' self.paddingMode = mode
def decryptFromBase64(self,entext): ''' 从base64编码字符串编码进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromBase64(entext) return self.__decrypt()
def decryptFromHexStr(self,entext): ''' 从hexstr编码字符串编码进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromHexStr(entext) return self.__decrypt()
def decryptFromString(self,entext): ''' 从字符串进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromString(entext) return self.__decrypt()
def decryptFromBytes(self,entext): ''' 从二进制进行AES解密 entext: 数据类型bytes ''' self.data = entext return self.__decrypt()
def encryptFromString(self,data): ''' 对字符串进行AES加密 data: 待加密字符串,数据类型为str ''' self.data = data.encode(self.characterSet) return self.__encrypt()
def __encrypt(self): if self.mode == AES.MODE_CBC: aes = AES.new(self.key,self.mode,self.iv) elif self.mode == AES.MODE_ECB: aes = AES.new(self.key,self.mode) else: print("不支持这种模式") return
data = self.__paddingData(self.data) enData = aes.encrypt(data) return MData(enData)
def __decrypt(self): if self.mode == AES.MODE_CBC: aes = AES.new(self.key,self.mode,self.iv) elif self.mode == AES.MODE_ECB: aes = AES.new(self.key,self.mode) else: print("不支持这种模式") return data = aes.decrypt(self.data) mData = MData(self.__stripPaddingData(data),characterSet=self.characterSet) return mData
if __name__ == '__main__':# 默认密钥 key = b"e45e329feb5d925b" iv = b"0000000000000000"    aes = AEScryptor(key,AES.MODE_CBC,iv,paddingMode= "PKCS5Padding",characterSet='utf-8')    # 选择文件目录 directory = './'    # 设置响应和请求解密文件 for i in range(10001): if i == 0: filename = 'cont.php' else:            filename = f'cont({i}).php'        file_path = os.path.join(directory, filename) if os.path.isfile(file_path): with open(file_path, 'r', encoding='utf-8') as file: content = file.read() rData = content                rData = aes.decryptFromBase64(rData)   if '"msg"' in str(rData): if not isinstance(rData, str): rData = str(rData) msg_pattern1 = r'"msg":"(.*?)"' match_response = re.search(msg_pattern1, rData)                    msg_value_response = match_response.group(1)                      decoded_msg_response = base64.b64decode(msg_value_response).decode('utf-8') print("响应内容是:",decoded_msg_response) input()                elif '64_decode' in str(rData): if not isinstance(rData, str):                        rData = str(rData)                     match_request = re.findall(r"'(.*?)'", rData)                    if match_request:                        msg_value_request = match_request[0].replace("'""").replace("[""").replace("]""")                        decoded_msg_request = base64.b64decode(msg_value_request).decode('utf-8') print("请求内容是:", decoded_msg_request) input() else:            print("密文不存在")


解密结果展示

DFIR蘇小沐
致力于电子数据取证(数字取证)与事件应急响应实战技术经验分享,计算机取证、手机取证、网络取证与犯罪调查、数据恢复、模糊图像增强、司法鉴定等技术研究【蘇小沐】
 最新文章