AWVS 12
下载
下载试用版(2020-05-12:下载链接已失效,需要邮箱重新注册)
准备工作
安装好后,登录并扫描网站,在事件窗口会看到相关信息:
所有的文件保存在本地的file:///C:\\ProgramData\\Acunetix Trial\\shared\\scans\\2e0a7e18-0b84-4e2e-a85c-fc3358d06769.zip
压缩包下,切换到目录下,解压文件,可以看到三个文件:
用sublime打开logfile.csv
文件,可以查看到命令:
观察上面的命令格式,可以提取命令,简化后的执行命令如下:
"C:\Program Files (x86)\Acunetix Trial\12.0.190927120\wvsc.exe" /scan https://www.baidu.com /log logfile.txt
以及日志部分的关键字提示符:
running in demo mode
去掉 demo 版本的限制
以管理员权限运行x64dbg,打开wvsc.exe
程序,然后更改命令为上面的简化命令:
然后运行至EntryPoint
,搜索字符串 running in demo mode
,在上面的切入点下断点:
找到当前目标地址值为1,但是需要它为0才能不显示 running in demo mode
:
查找一下 [r13+8c8]
的引用:
观察引用情况,数据段赋值操作有两条
00007FF7FBF5AB4B | 8886 C8080000 | mov byte ptr ds:[rsi+8C8],al |
00007FF7FC34C670 | C683 C8080000 01 | mov byte ptr ds:[rbx+8C8],1 |
在两个地址下断,观察数据后,第一条 al
中的值为0,第二条为赋值为1,因此找到关键点:
修改代码,将数据值赋值为0后,运行到切入点,发现跳过 running in demo mode
:
查看扫描结果,是否存在请求信息:
去掉时间限制
修改本地时间,使其与北京时间不同,但请注意不要超过 license_info.json
中的时间,不然会有报错!(后期测试加:第一次更换时间,可以使用, 但是我后来重新更换了时间,发现web页面会报错,提示 too many request
显然这是一个暗桩,但我并没有什么方式可以绕过,本地使用 wvsc
进行扫描时,并未报错,那么就有可能是存在另一个校验程序。)
关键字符串:
system time moved back
搜索字符串,找到切入点:
进入函数下断,重载寻找 al
的修改地址:
手动修改时发现,多次调用该函数进行判断,所以直接修改代码为 xor rax,rax
:
测试结果:
去除 license 覆写
功能 点上面已经破解的差不多了,接下来需要对 license 进行分析。
找到了一些关键字:
"license_info.json"
"license_usage.json"
"license_info.json"
"license_usage.json"
"failed to write License Info File"
在文件夹中搜索其中的字符串,
接下来,在 failed to write License Info File
上方函数下断点。编辑 license_info.json
,尝试是否会断下:
修改函数代码如下:
修改 license_info.json
内容并刷新页面,显示如下:
生成 license
打开火绒剑,在不启动扫描的情况下,上一步的页面,看到opsrv.exe
进程一直在调用wvsc.exe
猜测 lincense
的校验部分由该程序进行,将文件拖入x64dbg,进入到 EntryPoint
,发现特征代码如下:
入口OEP属于是 pyinstaller
构建的exe程序的OEP特征,
这里用命令生成一个exe文件用来做对比,命令如下:
pyinstaller get_checksum.py
还有一个特征方式可以鉴别:搜索字符串 pyinstall
:
针对python打包的exe文件,可以使用 pyinstxtractor + uncompyle6
来进行反编译。
pyinstxtractor.py
github下载 ,然后使用如下命令,拆解 exe
为 pyc
:
python pyinstxtractor.py opsrv.exe
如果运行出现如下错误,请去sourceforge下载,两个下载点下载的代码是不一样的,一个存在严格的版本校验,另一个则没有。
失败与成功截图对比如下:
在解压后的文件中,搜索 license
结果如下:
根据名字判断 helpers.licensing.licenses.on_premise.pyc
和 helpers.licensing.licenses.pyc
可能性比较大,逐个打开看看。
接下来用 uncompyle6
反编译 pyc
文件
首先使用命令安装(管理员权限):
pip install uncompyle6
反编译命令如下:
uncompyle6 helpers.licensing.licenses.on_premise.pyc >licenses.on_premise.py
寻找关键函数:
初步阅读代码,可以发现这是根据 license_data
来拼凑一个字符串用来加密生成 checksum
文件,再与 license_data
中的 checksum
对比,如果不一样报错。 license_data
中的信息和 license_info.json
文件中的结构是一样的,估计是读取的这个文件,根据上下文定位判断函数内相关引用和调用来源:
查看上面的引用来源:
通过反编译两个 pyc
文件逐步获取加载 license_data
和生成 checksum
的 md5
算法,源代码最终结合的 get_checksum.py
代码如下:
"""
get_checksum (Supports AWVS 12)
Author : Alee
Date : 28-Mar-2020
"""
import hashlib
import json
class ActivationErrorCodes:
NO_WARNINGS = 0
SITE_CHANGED = 1
CHECKSUM_MISMATCH = 2
INVALID_EXPIRY_DATES = 4
EXPIRED = 8
MAINTENANCE_EXPIRED = 16
MAINTENANCE_WILL_EXPIRE = 32
WITHIN_GRACE = 64
MAC_CHANGED = 128
KEY_NOT_FOUND = 256
KEY_NOT_ENABLED = 512
KEY_VERSION_MISMATCH = 1024
ENGINE_COUNT_MISMATCH = 2048
TARGET_COUNT_MISMATCH = 4096
PRODUCT_CODE_NOT_FOUND = 8192
SYSTEM_TIME_MOVED_BACK = 16384
ERP_NOT_REACHED = 32768
ERP_RESPONSE_ERROR = 65536
CONCURRENT_REACT = 131072
ACUMONITOR_FAILED = 16384
ERROR_LOADING_ACTIVATION_JSON = 524288
FILE_MISSING_OR_CORRUPT = 1048576
@classmethod
def get_keys(cls, code):
result = []
for key in cls.__dict__.keys():
value = getattr(cls, key)
if key[:1] != '_' and not callable(value) and value & code:
result.append(key)
return result
def md5(value: str, salt: str=None) -> str:
m = hashlib.md5()
if salt:
m.update(salt.encode('utf-8'))
m.update(value.encode('utf8'))
return m.hexdigest()
def get_license_checksum(license_data):
activation_error = license_data.get('activation_error', 0)
if isinstance(activation_error, str):
activation_error = int(activation_error)
secret = '25128b0a92d51e6bf4ea7a40b91b33be911144f7'
canonical_form = ''.join((
secret,
license_data.get('license_key', ''),
license_data.get('product_code', ''),
'%06d' % (license_data.get('max_targets', 0),),
'%06d' % (license_data.get('max_engines', 0),),
license_data.get('bxss_user_id', ''),
license_data.get('bxss_api_key', ''),
'true' if license_data.get('offline', False) else 'false',
'%06d' % (license_data.get('major_version', 0),),
'%06d' % (license_data.get('minor_version', 0),),
'%06d' % (license_data.get('build_number', 0),),
'true' if license_data.get('expired') else 'false',
'true' if license_data.get('maintenance_expired') else 'false',
'%06d' % (activation_error,),
'true' if license_data.get('activated') else 'false',
'true' if license_data.get('scan') else 'false',
'true' if license_data.get('update') else 'false',
'true' if license_data.get('access') else 'false',
'%d' % (license_data.get('grace', 0),)))
checksum = md5(canonical_form)
print('canonical_form:'+canonical_form)
print('checksum:'+checksum)
print('license_data.get(\'checksum\'):'+license_data.get('checksum'))
def get_license():
jsonPath = './license_info.json'
license_info = json.loads(open(jsonPath, 'rt', encoding='utf-8').read())
license_info = dict(license_info)
info = license_info.get('info')
if info is not None:
if isinstance(info, dict):
del license_info['info']
license_info.update(info)
license_info['offline'] = license_info.get('is_offline', False)
license_info['activation_error_codes'] = ActivationErrorCodes.get_keys(int(license_info.get('activation_error', '0')))
return license_info
license_data = get_license()
get_license_checksum(license_data)
代码存在很大的优化空间。
下面给出 license_info.json
文件的模板,其中 AOPENT
为企业版标识,请不要修改。其他的内容,可以根据上方算法进行修改。
{
"info": {
"license_key": "TRY-TO-CRAKED-ALEE",
"company": "REVERSE",
"name": "ALEE",
"phone": "666666",
"email": "crake@crake.com",
"country": "earth",
"first_activated": "2020-01-17T10:53:50",
"expires": "2100-01-31T10:53:50",
"maintenance_expires": "2100-01-31T10:53:50",
"max_engines": 999999,
"max_targets": 999999,
"product_code": "AOPENT",
"bxss_user_id": "",
"bxss_api_key": "",
"is_offline": false,
"is_worker": false
},
"major_version": 12,
"minor_version": 0,
"build_number": 190927120,
"expired": false,
"maintenance_expired": false,
"scan": true,
"update": true,
"access": true,
"activation_error": 0,
"activated": true,
"checksum": "17721cb9bc44780a55f52aedde975737",
"grace": -1,
"last_reactivated": "2020-01-31T10:53:50"
}
请将 get_checksum.py
和 license_info.json
放在同一文件夹下!运行结果如下:
验收阶段
结束
AWVS 13
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2020-05-13 新增AWVS13专业版权限绕过方式 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
来源
安装包来源于同事,同事来源与互联网,版本:13.0.200205121
准备工作
调试命令:
"C:\Program Files (x86)\Acunetix\13.0.200205121\wvsc.exe" /scan https://www.baidu.com /profile Default /log C:\Users\Alee\Desktop\logfile.txt
尝试过不加 /profile Default
不会正常进行扫描任务。
去掉license覆写
运行任务,报错信息返回在指定的日志文件中,信息如下:
2020-05-13T10:49:41.218516 INFO MAIN 8952 command line "C:\Program Files (x86)\Acunetix\13.0.200205121\wvsc.exe" /scan https://www.baidu.com /profile Default /log C:\Users\Alee\Desktop\logfile.txt
2020-05-13T10:55:37.513990 ERROR activator 8952 Error: License File could not be loaded
2020-05-13T10:55:39.435225 ERROR MAIN 8952 Licensing Error
2020-05-13T10:55:39.436958 INFO MAIN 8952 application exiting
在x64dbg中搜搜当前区域字符串引用 License File could not be loaded
、Licensing Error
总共三处
观察license文件,提示 Licensing Error
时,文件就会被重写。
配合搜索当前区域的字符串 failed to write License Info File
定位到 call 0x00007FF79A429B70
就是重写函数。
将定位函数的代码重写为
mov eax,1
ret
一石二鸟
重写函数处理掉以后,就是第一条License File could not be loaded
,搜索字符串下断,重新回到Licensing Error
提示的上下文片段,发现了一个有趣的地方:
上方的条件call,竟然包含了License File could not be loaded
的报错,还包含了很多的报错信息could not decode / decrypt license data
可以推测就是证书校验函数了,重写尝试:
mov eax,1
ret
居然正常运行了,看来把鸡蛋放在一个篮子里,还是挺好的。
license改动
license的算法并未修改,不过需要修改一些版本地址之类的地方,提供一份可用license:
{
"info": {
"license_key": "TRY-TO-CRAKED-ALEE",
"company": "REVERSE",
"name": "ALEE",
"phone": "666666",
"email": "crake@crake.com",
"country": "earth",
"first_activated": "2020-05-13T10:53:50",
"expires": "2100-05-13T10:53:50",
"maintenance_expires": "2100-05-13T10:53:50",
"max_engines": 999999,
"max_targets": 999999,
"product_code": "AOPENT",
"bxss_user_id": "",
"bxss_api_key": "",
"is_offline": false,
"is_worker": false
},
"major_version": 13,
"minor_version": 0,
"build_number": 200205121,
"expired": false,
"maintenance_expired": false,
"scan": true,
"update": true,
"access": true,
"activation_error": 0,
"activated": true,
"checksum": "31baf19e58d82c2c9f166371cc593a21",
"grace": -1,
"last_reactivated": "2020-05-13T10:53:50"
}
结语
没想到13不仅仅是丰富了漏洞库,优化了扫描界面,就连注册
也更加方便了。