2014-08-25

英雄联盟无限视距实现代码

原理

游戏进程League of Legends.exe中某个偏移记录着视距值,游戏根据这个数值设置你的当前游戏的屏幕分辨率,最大只能达到屏幕的极限分辨率那么大,所以一旦调的太大了外面只能看到黑色一片。

实现方法

修改的方法也分两种,一种是将进程文件League of Legends.exe打开直接搜索其中默认的Float类型视距值2250,将其改为最大值4450,最大值其实没有上限,不过改的再大也是4450的效果。这种方法有个缺点,就是游戏一旦更新,这个进程文件改变,相应的偏移位置也会发生变化,所以还要重新去修改,比较麻烦。

这里说第二种方法,游戏运行起来后,动态从内存中搜索到视距值的位置,直接通过注入的方式,将视距值动态写进去,为减少对游戏的影响,只在进入游戏界面的时候进行修改操作,游戏结束后将原始视距值复原。并且为了兼容以后视距原始值以及修改最大值的变更,将这些参数全写入配置文件,想更改只需要修改配置文件里相应的值即可。

实现代码

主流程逻辑


#include "stdafx.h"
#include "SightFunc.h"
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
/*
#define oldSightValue 0x450CA000  //2250
#define newSightValue 0x457A0000  //4000
#define newSightValue 0x453B8000  //3000   
#define newSightValue 0x458B1000  //4450   
*/
int _tmain(int argc, _TCHAR* argv[])
{
    WinInit();
    int flag = 0;
    BOOL isNewSight = FALSE;
    DWORD dwProcId = 0;
    HANDLE hProcess = NULL;
    BYTE *pTagModBaseAddr = NULL;
    // 如果已有互斥量存在则释放句柄并复位互斥量
    HANDLE m_hMutex = CreateMutexW(NULL, FALSE, L"idhyt");
    if (ERROR_ALREADY_EXISTS == GetLastError())
    {
        wprintf(L"The Program has been running! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }
    //L"SeDebugPrivilege"
    EnablePrivilege(SE_DEBUG_NAME, TRUE);
    dwProcId = GetProcessIdByName(L"League of Legends.exe");
    if (0 == dwProcId)
    {
        wprintf(L"Get ProcessID error! ErrorCode: %d\n", GetLastError());
        printf("选择完英雄,进入读条状态时再启用!\n\n");
        goto gleave;
    }
    hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcId);
    if (NULL == hProcess)
    {
        wprintf(L"Get ProcessHandle error! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }
    pTagModBaseAddr = GetProcessModBaseAddr(L"League of Legends.exe", dwProcId);
    if (NULL == pTagModBaseAddr)
    {
        wprintf(L"Get pTagModBaseAddr error! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }
    PINI_ProfileInfor pIniProfileInfor = GetPrivateProfileInfor();
    if (pIniProfileInfor->bRet == TRUE)
    {
        while (true)
        {
            Sleep(1000);
            dwProcId = 0;
            hProcess = NULL;
            pTagModBaseAddr = NULL;
            isNewSight = FALSE;
            dwProcId = GetProcessIdByName(L"League of Legends.exe");
            if (0 == dwProcId)
            {
                if (flag == 0)
                {
                    printf("等待进入游戏... ErrorCode: %d\n", GetLastError());
                }
                flag ++ ;
                continue;
            }
            hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcId);
            if (NULL == hProcess)
            {
                wprintf(L"Get ProcessHandle error! ErrorCode: %d\n", GetLastError());
                continue;
            }
            pTagModBaseAddr = GetProcessModBaseAddr(L"League of Legends.exe", dwProcId);
            if (NULL == pTagModBaseAddr)
            {
                wprintf(L"Get pTagModBaseAddr error! ErrorCode: %d\n", GetLastError());
                continue;
            }
            isNewSight = SetNewSight(hProcess, pTagModBaseAddr, pIniProfileInfor->newSight, pIniProfileInfor->oldSight, pIniProfileInfor->dwRVA);
            if (isNewSight == TRUE)
            {
                wprintf(L"Chage Sight Success!\n");
                Sleep(3000);
                ShowWindow(GetConsoleWindow(), SW_HIDE);
            }
            else
            {
                wprintf(L"Chage Sight False!\n");
                continue;
            }
            while (GetProcessIdByName(L"League of Legends.exe"))
            {
                Sleep(5000);
            }
            Sleep(3000);
            ShowWindow(GetConsoleWindow(), SW_SHOW);
            flag = 0;
            system("cls");
            printf("若继续使用无限视距功能,请勿关闭该窗口!\n");
        }
    }
gleave:
    if (hProcess)
        CloseHandle(hProcess);
    if (m_hMutex)
        CloseHandle(m_hMutex);
    EnablePrivilege(SE_DEBUG_NAME, FALSE);  
    system("pause");
    return 0;   
}

用到的功能函数


// SightFunc.cpp
#include "stdafx.h"
#include "SightFunc.h"

DWORD WINAPI GetProcessIdByName(LPCTSTR lpFilename)
{
    HANDLE hProcessSnap = NULL; 
    DWORD dwProcessId = 0;
    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(PROCESSENTRY32); 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    if(hProcessSnap == INVALID_HANDLE_VALUE) 
        return 0;
    if(!Process32First(hProcessSnap,&pe32))
    {
        int i = GetLastError();
        CloseHandle(hProcessSnap);
        return 0;
    }

    do
    {
        if(0 == _wcsicmp(pe32.szExeFile,lpFilename))
        {
            dwProcessId = pe32.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hProcessSnap,&pe32));

    CloseHandle(hProcessSnap);
    return dwProcessId;
}

//返回进程指定模块基址
BYTE *GetProcessModBaseAddr(LPCTSTR lpFilename, DWORD dwPID) 
{ 
    HANDLE hModuleSnap = INVALID_HANDLE_VALUE; 
    MODULEENTRY32 me32 = {0};
    me32.dwSize = sizeof( MODULEENTRY32 ); 
    BYTE *pModBaseAddr = NULL;
    hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID ); 
    if(INVALID_HANDLE_VALUE == hModuleSnap) 
    {
        wprintf(L"CreateToolhelp32Snapshot (of modules %d) Error! ErrorCode: %d", dwPID, GetLastError());
        goto gleave;
    }    
    
    if( Module32First( hModuleSnap, &me32 )) 
    { 
        do 
        { 
            if(0 == _wcsicmp(me32.szModule,lpFilename))
            {
                pModBaseAddr = me32.modBaseAddr;
                break;
            }
            
        } while( Module32Next( hModuleSnap, &me32 ) ); 
    }
    else
    { 
        wprintf(L"Module32First Error! ErrorCode: %d", GetLastError()); 
        goto gleave;
    } 
gleave:
    if (hModuleSnap)
        CloseHandle( hModuleSnap );
    return pModBaseAddr;
} 


/* 
功能:   是否开启Debug权限
参数:   lpName:"SeDebugPrivilege"
        fEnable:TRUE表示开启,反之禁用
返回值: TRUE/FLASE
SE_DEBUG_NAME SeDebugPrivilege
*/
BOOL EnablePrivilege(LPCTSTR lpszPrivilegeName, BOOL bEnable)
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp = {0};
    LUID luid = {0};
    BOOL bRet = FALSE;

    if(!OpenProcessToken(GetCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES |
        TOKEN_QUERY | TOKEN_READ,
        &hToken))
    {
        bRet =FALSE;
        goto gleave;
    }
    if(LookupPrivilegeValue(NULL, lpszPrivilegeName, &luid))
    {
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = luid;
        tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0;
        if (AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,NULL))
        {
            bRet = TRUE;
        }
    }
gleave:
    if (hToken)
        CloseHandle(hToken);
    return bRet;
}

VOID WinInit()
{
    system("color 02 & title 英雄联盟无限视距修改器");
    time_t timer; 
    time(&timer); 
    struct tm newtime;
    localtime_s(&newtime, &timer); 
    //wprintf(L"***********************************\n");
    wprintf(L"%d.%d.%d", newtime.tm_year+1900, newtime.tm_mon+1, newtime.tm_mday);
    printf("--> 右键->管理员身份运行,请低调使用!\n");
    printf("--> 默认视距4000,修改说明:Infinite Sight.ini\n");
    printf("--> 选完英雄,进入游戏读条界面以后使用!\n");
    printf("--> 勿传播,勿做非法用途!!! \n");
    printf("                Date 2014.08.25 \n\n");
    printf("--------------------------------------\n\n");
}

//获取配置文件信息
PINI_ProfileInfor GetPrivateProfileInfor()
{
    DWORD bRet = 0;
    PINI_ProfileInfor pIniProfileInfor = new INI_ProfileInfor;
    memset(pIniProfileInfor, 0, sizeof(INI_ProfileInfor));
    WCHAR strPath[MAX_PATH] = {0};
    WCHAR strIniPath[MAX_PATH] = {0};
    DWORD newSight = 0;
    DWORD oldSight = 0;
    DWORD dwRVA = 0;
    bRet = GetCurrentDirectoryW(MAX_PATH,strPath);
    if (bRet > MAX_PATH)
    {
        pIniProfileInfor->bRet = FALSE;
        wprintf(L"GetCurrentDirectory Error! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }
    swprintf_s(strIniPath, MAX_PATH, L"%s%s", strPath, L"\\Infinite Sight.ini");

    newSight = GetPrivateProfileInt(L"New Sight", L"newSight", 0, strIniPath);
    if (2250 > newSight || 4000 < newSight)
    {
        pIniProfileInfor->bRet = FALSE;
        wprintf(L"The newSight Value Error!\n");
        goto gleave;
    }
    oldSight = GetPrivateProfileInt(L"Old Sight", L"oldSight", 0, strIniPath);
    if (2250 != oldSight)
    {
        pIniProfileInfor->bRet = FALSE;
        wprintf(L"The oldSight Value Error!\n");
        goto gleave;
    }
    dwRVA = GetPrivateProfileInt(L"Error", L"Value", 0, strIniPath);
    if (0 == dwRVA)
    {
        pIniProfileInfor->bRet = FALSE;
        wprintf(L"The dwRVA Value Error!\n");
        goto gleave;
    }
    pIniProfileInfor->newSight = float2hex(newSight);
    pIniProfileInfor->oldSight = float2hex(oldSight);
    pIniProfileInfor->dwRVA = dwRVA;
    pIniProfileInfor->bRet = TRUE;
gleave:
    return pIniProfileInfor;
}

DWORD float2hex(float f)
{
    DWORD h = 0;
    char *pf = (char*)&f;
    if (pf)
    {
        h = *(DWORD *)pf;
    }
    return h;
}

BOOL SetNewSight(HANDLE hProcess, BYTE *pTagModBaseAddr, DWORD newSightValue, DWORD oldSightValue, DWORD dwRVA)
{

    BOOL bRet = FALSE;
    BOOL isNewSight = FALSE;
    DWORD dwSight = 0;
    DWORD dwRead = 0;
    DWORD dwNewSight = 0;
    DWORD dwWrite = 0;
    DWORD dwOldProtect = 0;
    DWORD dwInitProtect = 0;
    pTagModBaseAddr = pTagModBaseAddr + dwRVA;
    bRet = VirtualProtectEx(hProcess, pTagModBaseAddr, 0x4, 0x40, &dwInitProtect); //PAGE_EXECUTE_READWRITE 
    if (!bRet)
    {
        wprintf(L"Change PAGA_attribute error! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }   
    bRet = ReadProcessMemory(hProcess, pTagModBaseAddr, &dwSight, 4, &dwRead);
    if (!bRet)
    {
        wprintf(L"ReadProcessMemory error! ErrorCode: %d\n", GetLastError());
        goto gleave;
    }

    if (oldSightValue == dwSight && 4 == dwRead)
    {
        dwNewSight = newSightValue;
        bRet = WriteProcessMemory(hProcess, pTagModBaseAddr, &dwNewSight, 4, &dwWrite);
        if (!bRet)
        {
            wprintf(L"WriteProcessMemory error! ErrorCode: %d\n", GetLastError());
            goto gleave;
        }
        isNewSight = TRUE;
    }
    else
    {
        isNewSight = FALSE;
        wprintf(L"The Sight Value not found!\n");
    }    

    bRet = VirtualProtectEx(hProcess, pTagModBaseAddr, 0x4, dwInitProtect, &dwOldProtect); //0x40 PAGE_EXECUTE_READWRITE 
    if (!bRet)
    {
        wprintf(L"Resume PAGA_attribute error! ErrorCode: %d\n", GetLastError());
    }
gleave:
    return isNewSight;
}

配置文件模版

[说明]
文件名为:Infinite Sight.ini
默认视距为最大值4000,如需修改视距,改变new Sight值即可,如3000视距改为new Sight = 3000,修改值必须在2250-4000之间!
请勿移动本配置文件,需和Infinite Sight.exe在同目录下。
此插件为个人使用,请勿非法传播 :)

[New Sight]
newSight = 4000

[Old Sight]
oldSight = 2250

2013-07-01

CVE-2013-0422

CVE-2013-0422漏洞简略分析

简介

此漏洞属于JAVA7的设计缺陷,该漏洞主要利用了MBeanInstantiator类中FindClass函数能够查找到任何包内任何类的对象的功能,使得Applet获得了本来无权访问的类;
然后又利用了在JDK1.7中新增的动态实现细节中MethodHandles.Lookup类中findConstructor及findVirtual函数创建了Classloader,并加载了恶意类.

调试环境

本机:Eclipse;win7 x64;jre-7u5-windows-i586;Metasploit 4.6
虚拟机:win xp sp3;jre-7u5-windows-i586;IE8

Metasploit测试

1.首先在Metasploit里边做下漏洞利用测试,配置环境如下所示:

2.之后在虚拟机中IE8输入上边的URL地址之后在Metasploit内就可以得到shell窗口了。
地址:http://192.168.237.1:8080/SIBwjkLzdI

测试代码

首先看下正常情况下的本地测试,代码如下:

import java.io.IOException;   
import java.security.Permission;  
import java.security.PermissionCollection;  
import java.security.Policy;  
import java.security.ProtectionDomain;  
import java.util.Enumeration;  
import javax.swing.JOptionPane;

public class Test {  
    void checkPermission() {  
        ProtectionDomain domain = this.getClass().getProtectionDomain();  
        PermissionCollection pcoll = Policy.getPolicy().getPermissions(domain);  
        Enumeration e = pcoll.elements();  
        int i = 0;  
        for (; e.hasMoreElements();) {  
            Permission p = (Permission) e.nextElement();  
            System.out.println(i + ": " + p);  
            i++;  
        }  
        System.out.println("the num:" + i); 
    }

    void alert() throws IOException {  
        try {  
            Runtime.getRuntime().exec("calc.exe");  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
            throw e;  
        }  
    } 
    
    public static void main(String[] args) {
        try{
        // TODO Auto-generated method stub  
            Test test = new Test();
             // test.checkPermission(); 
            System.out.println("1.SecurityManager 开启前测试");  
            try {  
                test.alert();  
                System.out.println("2.成功执行 exec");  
            } catch (SecurityException e) {  
                System.out.println("2.you have no permission to exec");  
            }  
            System.out.println("3.开启SecurityManager");  
            System.setSecurityManager(new SecurityManager());  
            System.out.println("4.SecurityManager 开启后测试");  
            try {  
                test.alert();  
                System.out.println("5.成功执行 exec");  
            } catch (SecurityException e) {  
                System.out.println("5.you have no permission to exec");  
            } 
        }catch (Throwable e1) {  
            // TODO Auto-generated catch block  
            e1.printStackTrace();  
        }
    }
}

运行结果:
1.SecurityManager 开启前测试
2.成功执行 exec //弹出计算器
3.开启SecurityManager
4.SecurityManager 开启后测试
5.you have no permission to exec
可见,在开启SecurityManager的情况下是不具有执行exec权限的。

关键代码分析

该漏洞关键代码如下:

1.

由于MBeanInstantiator.findClass函数能够获得任意包中的任意类,因此其继承类
localMBeanInstantiator能够访问原本无权访问的sun.org.mozilla.javascript.internal.Context 及
sun.org.mozilla.javascript.internal.GeneratedClassLoader类;
其中,localClass1 及localClass2分别是两个类的引用.

2.

根据Java7中MethodHandles的动态调用功能, MethodHandles.Lookup 类中包含findVirtual(), findConstructor(), findStatic() , findSpecial() 四个函数。第 三 行localLookup.findVirtual 函 数返回了MethodHandles.Lookup类的findConstructor 函数,指其定类型为localMethodHandle1;之后指定locaMethodType2 的类型为Void型。

由于localMethodHandle1为MethodHandles.Lookup的findConstructor函数句柄,localClass1 为MBean中已经加载的sun.org.mozilla.javascript.internal.Context对象的引用,故调用invokeWithArguments函数即为在JVM中寻找sun.org.mozilla.javascript.internal.Context类的构造函数,返回该类构造函数的句柄为localMethodHandle2, 于是接下来的调用localMethodHandle2.invokeWithArguments即是创建sun.org.mozilla.javascript.internal.Context类的实例对象localObject1.至此通过
MBeanInstantiator类与MethodHandles类,实现了在Applet类中访问原本无权访问
的sun.org.mozilla.javascript.internal.Context类的目的.

3.

方法与上面2相同,localMethodHandle3为MethodHandles.Lookup类findVirutal()函数句柄,类型为localMethodType3。

通过findVirtual函数得到sun.org.mozilla.javascript.internal.Context类的createClassLoader函数,localMethodHandle4即为createClassLoader句柄;

通过调用localObject1的createClassLoader函数创建了一个新的classLoader. localObject2为ClassLoader的实例.

通过MethodHandles.Lookup函数的findVirtual()查找到sun.org.mozilla.javascript.internal.GeneratedClassLoader类的defineClass函数,函数类
型由localMethodType5 指定.此时localMethodHandle5 为defineClass函数类型.

通过调用localObject2类的defineClass函数将arrayOfByteBuff加载到JVM中,并创建为对象
localClass3,此时localClass3具有与localObject2相同的系统权限.

4.当执行localClass3.newInstance 时,在其实现中去掉了系统的SecurityManager,到此完
全关闭了系统的安全策略,从而实现了exploit.

2013-03-21

CVE-2013-0641

CVE-2013-0641 Acrobat Reader sandbox escape 调试笔记

调试环境:

Windows xp sp3_en & Adobe Reader 11.0.1 & Windbg & IDA

漏洞简介

Acrobat Reader自从引入沙盒功能以来,针对其的漏洞攻击就急剧减少,表明沙盒对攻击门槛的提高帮助不少。门槛提升不代表无法攻击,最近流传的通过spear phishing邮件发送pdf附件的攻击就是利用了2个漏洞,第一个利用xfa漏洞rop shellcode加载恶意dll后,再次利用沙盒漏洞,在broker process进程执行最终的恶意代码。

沙箱逃离流程

整个逃离过程全是在D.T dll中完成,该DLL由sandbox process触发xfa漏洞之后shellcode加载,流程如下图所示:

1.通过函数RegisterClipboardFormatW注册剪切板格式,构造0×80个dword,dword全为0×8080020。

2.触发broker process进程分配大小0xC800000内存,其中布置好rop shellcode,同时站位0×8080020附近范围的内存。

3.D.T构造调用GetClipboardFormatNameA的lpc buffer,同时更改lpc buffer对应要调用的tag变成0×73,0×73为GetClipboardFormatNameW的调用tag,broker process进程调用GetClipboardFormatNameW函数获取format,由于原本GetClipboardFormatNameW参数buffer里面填充了0×9c大小的0×42,且是单字节,但是实际的拷贝长度为0×9c×2,导致覆盖了后面的函数指针。

4.D.T构造tag id 0xb0的调用,最终在acrod32.exe 地址AcroRd32+0×9afda控制EIP,跳至0×808002c处执行已经布置好的shellcode, 该shellcode加载L2P.T dll后完成此次攻击。

调试过程

Step1: 注册ClipboardFormat

breakpoit

sandbox process: 
0 e 089c1d7c     0001 (0001)  0:**** D+0x1d7c

D.T通过user32!RegisterClipboardFormatW注册了一个clipboard format,format中构造数据为0x80个0x08080020(该地址为触发漏洞是指向的函数指针地址)。

因此注册新clipboard format的名称为0x80个0x08080020大小为200h,存放在lpszFormat中注册成功,返回表示注册的剪切板格式:0x0000c10a

RegisterClipboardFormat功能
注册一个新的剪贴板格式。这种格式可以被用来作为一个有效的剪贴板格式。
参数
lpszFormat []
类型:LPCTSTR    新的格式的名称
返回值类型:UINT
如果函数调用成功,则返回值标识已注册的剪贴板格式。
如果函数失败,返回值是零。

Step2: 构造ROP链,完成堆喷射

breakpoit

sandbox process:
1 e 089c227d     0001 (0001)  0:**** D+0x227d  /////加载clbcatq.dll函数
2 e 089c2978     0001 (0001)  0:**** D!initOLEcontainer+0x5ec  /////load("clbcatq.dll")
3 e 089c2a75     0001 (0001)  0:**** D!initOLEcontainer+0x6e9  /////构造rop
4 e 089c2b87     0001 (0001)  0:**** D!initOLEcontainer+0x7fb  ////拷贝木马路径到共享内存30830408
5 e 089c2ba4     0001 (0001)  0:**** D!initOLEcontainer+0x818  ///拷贝ROP 堆喷

ClipboardFormat注册过后返回到D+0x2266

089c2266 89c3            mov     ebx,eax
089c2268 680000800c      push    offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c226d e85e090000      call    D!initOLEcontainer+0x844 (089c2bd0)
089c2272 89c6            mov     esi,eax
089c2274 85f6            test    esi,esi
089c2276 750c            jne     D+0x2284 (089c2284)
089c2278 680000800c      push    offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c227d e8cb080000      call    D!initOLEcontainer+0x7c1 (089c2b4d)
089c2282 89c6            mov     esi,eax
089c2284 6a01            push    1
089c2286 680000800c      push    offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c228b 56              push    esi
...

在D+227d处会加载clbcatq.dll并检测其大小,利用clbcatq.dll的代码来构造ROP链,然后开辟共享内存,将ROP链堆喷到共享内存中,大小为0xc800000

Sub_10002940

加载过clbcatq.dll后会用clbcatq.dll中的代码构造ROP链,关键代码如下:

Loc_10002B0A

最后构造的ROP链如下:size=0x400

然后在Loc10002bb2—-Loc10002b97中调用call sub100030d0将L2P.T木马路径复制到共享内存中。
拷贝函数在text:1002b67 call sub
100030d0

拷贝过后将指针返回给eax=30830028(call sub_100030d0)

之后开始完成ROP链堆喷射在木马路径后边,总大小为0xc800000,从而能够在08080820处占坑,ROP链堆喷从0x30830428(3d030028-0c800000+400)到0x3d030028之间。函数如下:

Step3: 布局broker process内存

breakpoit

sandbox process:
5 e 089c1c23 0001 (0001) 0:**** D+0x1c23
brokerpocess:
0 e 00472c2c 0001 (0001) 0:**** AcroRd32+0x72c2c //AllocateHeap
断下以后--- 1 e 0040290b 0001 (0001) 0:**** AcroRd32+0x290b //HeapAlloc
2 e 00472c54 0001 (0001) 0:**** AcroRd32+0x72c54 //ReadProcessMemory
////3 e 0048d6b8 0001 (0001) 0:**** AcroRd32+0x8d6b8 //HttpSendRequestA

堆喷之后返回loc_10002284之后会调用IPC

Sandbox进程通过tag 0x5d调用 HttpSendRequestA函数,在调用HttpSendRequestA之前,broker进程对该调用进行检查,最后在D+0x1c28处调用call ipc读取相应内容到broker进程

调用tag 0x5d之前,此时 eax指向该IPC messager的结构头四个字节为标签0x5d,其中参数30830028对应的长度为0c800000

Call ipc之后被断下:

text:00472C2C Call sub_4218c1会调用AllocateHeap申请大小为0x0c800000大小的内存块,调用关系如下:

Call sub_4218c1---sub_4218CC---call loc_404FBA---loc_404FD1---call sub_4028C6--- loc_4028F7---loc_402902

HeapAlloc:

AcroRd32+0x290b:
0040290b ff155c734e00    call    dword ptr [AcroRd32+0xe735c (004e735c)] ds:0023:004e735c={ntdll!RtlAllocateHeap (7c9100a4)}
0:009> p
eax=02770020 ebx=0c800000 ecx=7c9101bb edx=003e0608 esi=00000000 edi=0c800000
eip=00402911 esp=01c4fc74 ebp=01c4fc80 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
AcroRd32+0x2911:
00402911 8bf8            mov     edi,eax

申请堆空间过后,返回堆指针eax;size=0c800000
dd eax

申请堆过后返回调用ReadProcessMemory函数,将申请的堆中写入大小为0x0c800000的数据。
ReadProcessMemory(00000148,30830028,02770020,0c800000,01c4fce8)

第一个参数是 进程句柄,由OpenProcess函数获取;
第二个参数是要读出数据的地址,存放着我们堆喷的ROP链;30830028
第三个参数是用于存放读取数据的地址,为申请的堆空间;02770020
第四个参数是 要读出的数据大小;0c800000
第五个参数是实际读出数据的大小;调用返回后将填写0c800000

写入数据完成过后

从而覆盖到0808082c处刚好是构造的ROP链

Step4: 触发漏洞,造成溢出

breakpoit

sandbox process:
6 e 089c1ea5 0001 (0001) 0:**** D+0x1ea5 //tag(0x73)
7 e 089c22be 0001 (0001) 0:**** D+0x22be //push 9c,call ipc(0x73)
broker process
4 e 00496352 0001 (0001) 0:**** AcroRd32+0x96352 //GetClipboardFormatNameW

D.T在布局完broke process内存之后,开始构造GetClipboardFormatNameA 函数,长度为9ch,然后调用的lpc buffer,具体函数sub_10001E23

0:006> dd eax
089ed43c  00000073 00000000 00000000 00000000
089ed44c  00000000 00000000 00000000 00000000
089ed45c  00000000 00000000 00000000 00000000
089ed46c  00000000 00000000 00000000 00000002
089ed47c  00000006 00000064 0000009c 00000002
089ed48c  00000100 00000004 ffffffff 00000104
089ed49c  ffffffff 42424242 42424242 42424242
089ed4ac  42424242 42424242 42424242 42424242

GetClipboardFormatNameA的tag 是0x74,但是改成0x73之后,broker process 进程并没有检查,对于broker process来说,参数的类型对应的函数ANSI或者UNICODE无法得知,在检查参数和大小一致性后,就开始调用tag指定的GetClipboardFormatNameW函数。
Call IPC之后被断下:

Breakpoint 2 hit
eax=01c4fdac ebx=00000000 ecx=00b638c0 edx=0f0ab140 esi=00b638c0 edi=01c4fdac
eip=00496352 esp=0106ff64 ebp=0106ff68 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
AcroRd32+0x96352:
00496352 8b450c          mov     eax,dword ptr [ebp+0Ch] ss:0023:0106ff74=0f0ab140

GetClipboardFormatNameW API

Function  //接受从剪贴板中的指定的注册格式的名称,将该名称复制到指定的缓冲区。
    Retrieves from the clipboard the name of the specified registered format. The function copies the name to the specified buffer.

Parameters
    format [in]
        Type: UINT
            The type of format to be retrieved. This parameter must not specify any of the predefined clipboard formats.
    lpszFormatName [out]
        Type: LPTSTR
        The buffer that is to receive the format name.
    cchMaxCount [in]
        Type: int
        The maximum length, in characters, of the string to be copied to the buffer. If the name exceeds this limit, it is truncated.

调用 call ds:GetClipboardFormatNameW时3个参数

参数说明:

Format:0x0000c10a   //step1注册
lpszFormatName:0x0f0b26ac //接受buffer
cchMaxCount:0x9c //size

buffer中的数据:

0:003> dd 0f0b26ac l40
0f0b26ac  42424242 42424242 42424242 42424242
0f0b26bc  42424242 42424242 42424242 42424242
0f0b26cc  42424242 42424242 42424242 42424242
0f0b26dc  42424242 42424242 42424242 42424242
0f0b26ec  42424242 42424242 42424242 42424242
0f0b26fc  42424242 42424242 42424242 42424242
0f0b270c  42424242 42424242 42424242 42424242
0f0b271c  42424242 42424242 42424242 42424242
0f0b272c  42424242 42424242 42424242 42424242
0f0b273c  42424242 42424242 42424242 0000c10a  // 0f0b26ac+0x9c=0f0b2748
0f0b274c  00000000 00000000 00000000 e478f5b3   
0f0b275c  ff140100 0050bf20 61e00000 00000000   //0f0b26ac+0x9c+0x18
0f0b276c  00000000 00000000 00000000 00000000
0f0b277c  00000000 00000007 00000000 00000000

初始的buffer是0x9c大小,传入的cchMaxCount也是0x9c,但是user32!GetClipboardFormatNameW实际数据拷贝大小为0x9c*2,从而覆盖了后续buffer的某些关键指针。执行过后覆盖某指针:

0:003> dd 0f0b26ac l40
0f0b26ac  08080020 08080020 08080020 08080020
0f0b26bc  08080020 08080020 08080020 08080020
0f0b26cc  08080020 08080020 08080020 08080020
0f0b26dc  08080020 08080020 08080020 08080020
0f0b26ec  08080020 08080020 08080020 08080020
0f0b26fc  08080020 08080020 08080020 08080020
0f0b270c  08080020 08080020 08080020 08080020
0f0b271c  08080020 08080020 08080020 08080020
0f0b272c  08080020 08080020 08080020 08080020
0f0b273c  08080020 08080020 08080020 08080020
0f0b274c  08080020 08080020 08080020 08080020
0f0b275c  08080020 08080020 08080020 08080020     // overflowed
0f0b276c  08080020 08080020 08080020 08080020
0f0b277c  08080020 08080020 08080020 08080020
0f0b278c  08080020 08080020 08080020 08080020
0f0b279c  08080020 08080020 08080020 08080020

Step5: 控制eip

breakpoit

sandbox process
8 e 089c2094     0001 (0001)  0:**** D+0x2094 //call ipc(0xb0)
broker process
5 e 00497230     0001 (0001)  0:**** AcroRd32+0x97230 //控制eip

D.T在溢出关键数据后,在Call sub10002044再次调用tag id 0xb0,触发调用先前布置被覆盖的某对象:接着上边的调用返回后会调用sub10002044

Call sub_10002044

在call sub_100018a3调用tag id 0xb0

0:006> dd eax
089ed43c  000000b0 00000000 00000000 00000000
089ed44c  00000000 00000000 00000000 00000000
089ed45c  00000000 00000000 00000000 00000000
089ed46c  00000000 00000000 00000000 00000001
089ed47c  00000002 00000058 00000004 ffffffff
089ed48c  0000005c ffffffff 000003ea 00000000
089ed49c  00000000 00000000 00000000 00000000
089ed4ac  00000000 00000000 00000000 00000000

调用call IPC之后被断下:

最后调用rop链加载木马L2P.T

补丁

Adobe已经推出补丁,最新的Reader 11.0.02 for xpsp3en对比发现tag 0x73在处理GetClipboardFormatNameW callback时对传入的cchMaxCount参数进行了处理,将长度先/2,再调用GetClipboardFormatNameW就不会发生溢出了。如下:

Android Root Zap Framework

‎ 1. Warning 请遵守GPL开源协议, 请遵守法律法规, 本项目仅供学习和交流, 请勿用于非法用途! 道路千万条, 安全第一条, 行车不规范, 亲人两行泪. 2. Android Root Zap Frame...