某pdf编辑器提权获取会员功能记录

某pdf编辑器提权获取会员功能记录

最近一直在看《INTEL开发手册卷3(中文版)》电子版,想在笔记中插入一些文档中的图片,但是截图得到的图片画质还不是很清晰,而且wps和某个pdf编辑器都会对保存图片这个操作进行收费,所以就想着逆一下这个编辑器试试,由于这种编辑器没有很强的保护,一路上只碰到个别难搞的问题,所以只花了两三个小时成功破解出来了,感觉挺有意思的,便记录下来。本文旨在提供有关软件安全和漏洞分析的一般信息,仅供教育和学术讨论之用。作者明确声明,本文中的任何内容、示例或分析均不应用于非法目的。

破解效果图:

image-20241021215554536

破解思路

首先,这种pdf编辑器既然有这种会员和普通用户制度,那么必然在启动程序的时候会有一个联网检验,判断你是否是会员;其次,当用户准备做一些会员专属功能时,程序有可能会做第二次校验,如果不是会员则提醒你充会员,所以有两种破解思路,第一种完美破解,把两种检测都过了,另一种是只破解后面的校验,实现一个普通用户提权。由于前者稍微麻烦一点,这里是用的是后者实现破解,但是这里将两种方式的逆向分析过程都记录了一下。

方式一

像这种典型的有网络通信验证会员的软件,往往在最开始都会进行一个网络通信,跟服务器端校验一下用户身份,所以可以从两个入口切入:

  1. 寻找vip/会员有关字符串

  2. 断点定位send/recv等发包接包函数

先从第一种方式尝试切入,xdbg附加寻找vip/会员字符串,搜索所有用户字符串得到一下信息

image-20241021191823931

对这些字符串全部下断,重新启动,发现没有断下,根据字符串信息可以知道这些字符串的出现都是跟会员到期时间相关,所以我们没有充会员自然没有调用这些字符串,但是猜测会员到期前会进行一个是否为会员的一个校验,于是通过ida找到这些字符串的代码处,通过栈回溯最终回溯到这个位置

image-20241021192359645

分析了一下show_vip_dlg函数功能,大致就是计算会员还有多久过期或者已经过期,然后设置文本告诉用户提醒续费等等,

image-20241021192914592

image-20241021193055759

分析完这个函数,分析了调用show_vip_dlg处的函数以及继续往上回溯的几个函数,发现都是在进行字符串的初始化,或者通过qt的某个类将字符串和函数绑定在一起了,下了几个可能是另存为功能的函数断点,发现没有断下来,果断放弃这条路。

接着从第二种方式出发,对send或者recv类似的函数下断点前,先用wireshark抓取启动程序时的流量

image-20241021214715715

分析915的流量包发现返回了两张图片,没什么用,继续看一眼770的流量包

image-20241021194628514

发现这个流量包貌似是在请求权限,向服务器请求pdf_edit权限,然后服务器返回的权限为空值,所以用户权限不够。注意到这里是用的是POST请求,所以直接到xdbg里找到 QNetworkAccessManager::post函数下断,成功断下,回溯一层在ida里分析一下

image-20241021195438504

image-20241021195527120

发现这个函数用了一个定时器制定了一个定时任务,每隔30秒向服务器发送报文请求用户所拥有的权限,并在某种条件下会任务循环退出。所以猜测这里可以通过hook ReadAll函数修改发送回来的权限列表,添加我们想要的权限,从而提权,由于QT和hook环境不在一块,这里就没实操尝试。

方式二

第二种方式是从另存为这个功能入手,尝试追踪实现这个功能的函数,并往上找到会员身份校验的函数,hook或者patch实现提权。首先通过右键点击生成一个菜单这个行为,可以知道这个过程可能调用了QMenu这个类,于是对这个函数下断,编辑器右键随便一张图片,成功断下,回溯找到母函数,IDA定位找到这个地方

image-20241021205345034

image-20241021205459690

可以看到箭头指向了几个关键的函数,分别是tr初始化用到的insert字符串,意味着右键菜单里的插入功能,还有为此设置的icon,以及为菜单增加三个QAction,制定Action用到的data变量以及连接对应的槽函数,可以看到这里是将三个槽函数都绑定到了onMenuEvent函数,猜测是一个槽函数的dispatch,至于拿什么变量分发,肯定就是QAction绑定的data变量,也就是前文用setData绑定的整型数。

为了确定这是右键图片的菜单,我们还需要找到图片另存为这个action,再回溯一层

image-20241021210146554

image-20241021210249317

发现了很多功能的字符串,对应的是以下功能

{CE3FEE46-8FAE-4705-9C1C-2BAAE0EA8501}

但是这个函数并没有出现明确的将图片另存为含义的字符串,我想看到的是save as类似的字眼,由于这个功能必须是右键图片才会有的选项,所以我猜测这个函数的会在之后进行一个判断,判断点击的是否为图片,但是接着往下看的时候并没有找到明显的if判断,又因为这里的槽函数是用的dispatch的方式,于是我们可以通过下断dispatch函数,从而找到指定的功能。对public: class QVariant __thiscall QAction::data(void) const函数下断,右键另存为,成功断下

image-20241021213254461

找到分发函数,动调往下找到另存为功能对应的case,定位到该case的函数

image-20241021213453737

image-20241021213557272

image-20241021213717148

分析得到save as函数实现过程以及checkVip函数,以及check失败得到的提示信息,因此只要过掉这个checkVip函数就可以破解出另存为功能,并且由于这个验证函数的使用很广泛,某些其他付费功能的检测也可以过掉。

所以只要将这个函数hook一下永远返回真即可。

主要代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool MyCheck()
{
return true;
}
void hookCheck() {
if (MH_Initialize() != MH_OK)
{
return;
}
DWORD64 pid = GetCurrentProcessId();
checkFuncAddr = GetModuleBase(pid, MOUDULENAME) + checkFuncOffset;
if (MH_CreateHook((LPVOID)checkFuncAddr, MyCheck, (void**)&oldCheck) != MH_OK)
{
return;
}
if (MH_EnableHook((LPVOID)checkFuncAddr) != MH_OK)
{
return;
}
}