Tags:流量分析
,正则匹配
,布尔盲注
,pyshark
,DASCTF
0x00. 题目
找到flag,格式为DASCTF{}
附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件
附件名称:202509_NBWS_logbool.zip
0x01. WP
1. 分析流量结构
初筛http协议,发现大量SQL注入请求,初步判断为SQL注入流量复现
从返回包来看,如果语句判断成立则会返回success
关键词,反之则只显示语句
重新浏览注入请求,发现只针对三个字段,分别是username
,password
和content
,那么只要解析其中一个也就能用同样脚本解析其他两个
2. 编写exp.py
提取数据
使用过滤表达式(http.request.full_uri contains "username") && (tcp.srcport == 80)
先对username
的返回包进行筛选,并套用之前编写的通用脚本框架,提取请求数据request.full_uri
和响应内容data-text-lines
import pyshark
import re
import urllib.parsestrTsharkPath = "D:\\=Green=\\Wireshark\\App\\Wireshark\\"
strCapPath = "logbool.pcapng"
# 分别筛选出username,password和content
strFomula='(http.request.full_uri contains "username") && (tcp.srcport == 80)'cap= pyshark.FileCapture(strCapPath, display_filter=strFomula,tshark_path=strTsharkPath)# 协议结构分析开始
print("协议结构分析开始...")
i=0
for layer in cap[1].layers:print("第",i+1,"层:",layer.layer_name)print(layer.field_names)i+=1
print("协议结构分析完成。")
print("=" * 16)
sResult=[]for pkt in cap:intRequestNumber = pkt.numberprint("\r\tFrame Number: %s" % str(intRequestNumber), end="")for layer in pkt.layers:if layer.layer_name == "http": # 指定协议层sURI=layer.get_field_value("request.full_uri") #指定字段sURI=urllib.parse.unquote(sURI)print("request.full_uri: ",sURI)if layer.layer_name =="data-text-lines":print("data-text-lines: ",str(layer))
运行结果如下:
D:\=CTF缓存=\20250919_市信息中心竞赛\logbool>python exp.py
协议结构分析开始...
第 1 层: null
['family']
第 2 层: ip
['version', 'hdr_len', 'dsfield', 'dsfield_dscp', 'dsfield_ecn', 'len', 'id', 'flags', 'flags_rb', 'flags_df', 'flags_mf', 'frag_offset', 'ttl', 'proto', 'checksum', 'checksum_status', 'src', 'addr', 'src_host', 'host', 'dst', 'dst_host', 'stream']
第 3 层: tcp
['srcport', 'dstport', 'port', 'stream', 'stream_pnum', 'completeness', 'completeness_rst', 'completeness_fin', 'completeness_data', 'completeness_ack', 'completeness_syn_ack', 'completeness_syn', 'completeness_str', 'len', 'seq', 'seq_raw', 'nxtseq', 'ack', 'ack_raw', 'hdr_len', 'flags', 'flags_res', 'flags_ae', 'flags_cwr', 'flags_ece', 'flags_urg', 'flags_ack', 'flags_push', 'flags_reset', 'flags_syn', 'flags_fin', 'flags_str', 'window_size_value', 'window_size', 'window_size_scalefactor', 'checksum', 'checksum_status', 'urgent_pointer', '', 'time_relative', 'time_delta', 'analysis', 'analysis_initial_rtt', 'analysis_bytes_in_flight', 'analysis_push_bytes_sent', 'payload']
第 4 层: http
['', 'response_version', 'response_code', 'response_code_desc', 'response_phrase', 'server', 'response_line', 'date', 'content_type', 'transfer_encoding', 'connection', 'content_encoding', 'response', 'request_in', 'time', 'request_uri', 'request_full_uri', 'chunk_size', 'chunk_data', 'chunk_boundary', 'file_data']
第 5 层: data-text-lines
['']
协议结构分析完成。
================Frame Number: 108471
request.full_uri: http://10.211.55.8/?id=1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>64 AND 'hiCz'='hiCz
data-text-lines: Layer DATA-TEXT-LINES
: SELECT * FROM ctfuser WHERE id='1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>64 AND 'hiCz'='hiCz' LIMIT 0,1</br></br>successFrame Number: 108512
request.full_uri: http://10.211.55.8/?id=1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>96 AND 'hiCz'='hiCz
data-text-lines: Layer DATA-TEXT-LINES
: SELECT * FROM ctfuser WHERE id='1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>96 AND 'hiCz'='hiCz' LIMIT 0,1</br></br>successFrame Number: 108553
request.full_uri: http://10.211.55.8/?id=1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>112 AND 'hiCz'='hiCz
data-text-lines: Layer DATA-TEXT-LINES
: SELECT * FROM ctfuser WHERE id='1' AND ORD(MID((SELECT IFNULL(CAST(username AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),1,1))>112 AND 'hiCz'='hiCz' LIMIT 0,1</br></br>... ...
3. 增加正则匹配,根据返回包特征调整注入字符的ASCII值
提取SQL注入操作的字符索引和对应的ASCII值,当返回包中包含success
内容时表示判断成立,即实际的ASCII=ASCII+1
调整exp.py
import pyshark
import re
import urllib.parsestrTsharkPath = "D:\\=Green=\\Wireshark\\App\\Wireshark\\"
strCapPath = "logbool.pcapng"
# 分别筛选出username,password和content
strFomula='(http.request.full_uri contains "username") && (tcp.srcport == 80)'cap= pyshark.FileCapture(strCapPath, display_filter=strFomula,tshark_path=strTsharkPath)# 协议结构分析开始
print("协议结构分析开始...")
i=0
for layer in cap[1].layers:print("第",i+1,"层:",layer.layer_name)print(layer.field_names)i+=1
print("协议结构分析完成。")
print("=" * 16)
sResult=[]for pkt in cap:intRequestNumber = pkt.numberprint("\r\tFrame Number: %s" % str(intRequestNumber))for layer in pkt.layers:if layer.layer_name == "http": # 指定协议层sURI=layer.get_field_value("request.full_uri") #指定字段sURI=urllib.parse.unquote(sURI)print(sURI)# SELECT * FROM ctfuser WHERE id='1' AND ORD(MID((SELECT IFNULL(CAST(content AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),60,1))>50 AND 'aOIj'='aOIj' LIMIT 0,1</br></br>success sT=re.findall(rf'(.*?)LIMIT 0,1\),(.*?),1\)\)>(.*?) AND(.*?)',sURI)intIndex=int(sT[0][1])intASCII=int(sT[0][2])if len(sResult)<intIndex:sResult.append('')if layer.layer_name =="data-text-lines":print(str(layer))if "success" in str(layer):intASCII=intASCII+1sResult[intIndex-1]=chr(intASCII)print("".join(sResult))
print("\r")# boolblob
同理,修改过滤表达式为(http.request.full_uri contains "password") && (tcp.srcport == 80)'
得到password
内容为timeemitloggol
4. 修正脚本,获取content
修改过滤表达式为(http.request.full_uri contains "content") && (tcp.srcport == 80)'
获取content
内容时发现脚本报错
... ...Frame Number: 41340
http://10.211.55.8/?id=1' AND ORD(MID((SELECT IFNULL(CAST(content AS NCHAR),0x20) FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),130,1)) ! = 53 AND 'aOIj'='aOIj
Traceback (most recent call last):File "D:\=CTF缓存=\20250919_市信息中心竞赛\logbool\exp.py", line 37, in <module>intIndex=int(sT[0][1])
出错语句提取的内容为!=
而不是>
,查看对应的请求和返回数据,仅仅是对某个字符爆破的过程请求,不影响最终结果,增加容错判断
... ...sT=re.findall(rf'(.*?)LIMIT 0,1\),(.*?),1\)\)>(.*?) AND(.*?)',sURI)# 如果正则未匹配到有效数据,忽略本次请求和响应if len(sT)<1:continueintIndex=int(sT[0][1])intASCII=int(sT[0][2])
... ...
最终得到content
内容为
526172211a0701003392b5e50a01050600050101808000b2b1f3d65502033cb00004a80020a09ebf0280030008666c61672e747874300100030fedd6cdb8cc2535d8819161a4c7f5b2b05912735d7a3fd51074b03e4d9a68c5ec47766f7c7e9491369e1d8add0a0302eefeaba7a446d80100cf48e5373135a4b7de8993121c592e8c87743c2e8c0fffb766b53217f07acac40b55cf1db67923bea02a55299c54991d77565103050400
5.HEX导入文件,顺利获得flag
将content
内容导入十六进制编辑器发现头部特征为Rar!
,另存为rar文件。
使用前期解析出来的password
解压后得到flag为DASCTF{33c12355cd508de6b9b3676b97ece17e}
0x02. 总结
由于平时代码写得不多前后调试花了半个小时,作为一个线下赛题,整体来说还是做得酣畅淋漓