分數:100 描述: 提示 1:key 不是大家喜歡的波波老師!
提示 2:bmp+png
提示 3:CRC Link: http://pan.baidu.com/s/1o6x4FEE
Bmp 只是誘餌,給大家看看波波老師。關鍵在于 png 圖片,根據 png 格式圖片,以及提示,很明顯可以想到利用 crc 去爆破寬度和高度。可以用 c 或者 py,py 在這題爆破三個字節還是可以的,如果字節太多還是用 c 吧。 之所以名字叫做 BP 斷點,是因為是 bmp+png 的圖片。
參考 c 代碼:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
//crc32.h
#ifndef _CRC32_H
#define _CRC32_H
UINT crc32( UCHAR *buf, int len);
#endif
static UINT CRC32[256];
static char init = 0;
//初始化表
static void init_table()
{
int i,j;
UINT crc;
for(i = 0;i < 256;i++)
{
crc = i;
for(j = 0;j < 8;j++)
{
if(crc & 1)
{
crc = (crc >> 1) ^ 0xEDB88320;
}
else
{
crc = crc >> 1;
}
}
CRC32[i] = crc;
}
}
//crc32實現函數
UINT crc32( UCHAR *buf, int len)
{
UINT ret = 0xFFFFFFFF;
int i;
if( !init )
{
init_table();
init = 1;
}
for(i = 0; i < len;i++)
{
ret = CRC32[((ret & 0xFF) ^ buf[i])] ^ (ret >> 8);
}
ret = ~ret;
return ret;
}
int main()
{
char sss[17]="\x49\x48\x44\x52\x00\x00";
int a1,a2,a3,a4,a5,a6,a7,a8;
int _crc32 = 0;
for (a3=0x00;a3<=0xff;a3++)
for (a4=0x00;a4<=0xff;a4++)
for (a7=0x00;a7<=0xff;a7++)
for (a8=0x00;a8<=0xff;a8++)
{
sss[6]=(char)a3;
sss[7]=(char)a4;
sss[8]='\x00';
sss[9]='\x00';
sss[10]=(char)a7;
sss[11]=(char)a8;
sss[12]='\x08';sss[13]='\x06';sss[14]='\x00';sss[15]='\x00';sss[16]='\x00';
sss[17]='\x00';
_crc32 = crc32((UCHAR *)sss, 17);
if(_crc32 == 0x80BF36CC)
{
printf("%s,%x,%x,%x,%x\n", sss,a3,a4,a7,a8);
}
}
return 0;
}
最后結果:
參考 python 代碼:
# -*- coding:utf-8 -*-import binascii
#注意返回的是小寫 形如:"0x1c4d1d3cL", 并且為字符串。
def CalcCrc32(str):
return hex(binascii.crc32(str) & 0xFFFFFFFF)
if __name__=="__main__":
str1 = '\x49\x48\x44\x52\x00\x00\x01'
str2 = '\x00\x00'
str3 = '\x08\x06\x00\x00\x00'
int1 = 0
int2 = 0
int3 = 0
for int1 in range(0,256):
for int2 in range(0, 256):
for int3 in range(0, 256):
m = str1 + chr(int1) + str2 + chr(int2) + chr(int3) + str3
if (CalcCrc32(m) == "0x80bf36ccL"):
print "Yeah, U Found it!"
print hex(int1)
print hex(int2)
print hex(int3)
exit()
跑幾分鐘后,結果就出來了。
先下載壓縮包 http://pan.baidu.com/s/1bncmvuR 解壓后發現兩個文件Music.exe
,Readme.doc
. 要求利用 xp sp3 對目標程序進行溢出 既然是比賽,那么肯定就是要有溢出點的啦 直接打開 exe,聽到了甩蔥歌 于是不準備關掉他了, 新開一個例程,OD 載入
發現是 VC6 鏈接的 于是就果斷去找 main 函數了 不過 exe 程序居然自殺了,不能聽甩蔥歌,差評 果斷定位到了 main
寫入內容為 exe 的啟動參數 于是猜想啟動參數這個地方應該就是所謂的溢出點 于是寫了一個簡單程序,界面如下
最后發現 273 程序就會崩潰 于是使用 273 創建進程 OD 附加并設置 F2 斷點于 ReadFile 于多次返回后定位到
觀察堆棧后發現 此時已覆蓋[esp]一個字節 Ret 后肯定會跳轉到內存中某一被修改的位置執行代碼 于是構建 shellcode 內存收縮
FF E4 (jmp esp)
最終定位到 0x77D29353
,打開 010 editor
,構建 exp.
接下來構造功能代碼 之后我發現程序段里竟然有可恥的 call eax 找了個地址比較吉利的構造了下 用阿爾法轉換了下 shellcode 最終 exp 如下
分數:300 描述: 提示 1:DES
提示 2:凱撒
得到兩串二進制后,轉換為 16 進制分別為:
0x4d3259784e7a49304f444a6f4e57746f4e44597a61575a6d4d3267785a6a5a6e5a6d59344d4763344f544d354f4441784f5774724e4449796144566d4f446730L
0x5379636c30763372L
轉換為字符串分別為:
M2YxNzI0ODJoNWtoNDYzaWZmM2gxZjZnZmY4MGc4OTM5ODAxOWtrNDIyaDVmODg0
Sycl0v3r
可以認為第一個字符串為 DES 的密文,第二個字符串為 DES 的密鑰。解密發現不對。但是根 據大小寫和數字可以猜測為 base64,解密后的字符串:
3f172482h5kh463iff3h1f6gff80g89398019kk422h5f884
再用 DES 的密鑰解密,發現還是不對。根據提示“凱撒”去解碼試試。
http://crypo.in.ua/tools/eng_caesar.php
解幾次后發現 3a172482c5fc463daa3c1a6baa80b89398019ff422c5a884
這串是可以用DES 解密的。
在輸出”tell me:”之后程序會讀入用戶輸入,但這里使用了危險的 scanf(“%s”)而沒有任何額外 的檢查,直接輸入超長字符串就可以造成 buffer overflow。
成功利用彈 MessageBox 的截圖如下
P.S:其實程序里還有一個格式化字符串漏洞,不過那個利用起來沒這個方便,所以就直接 用這個了。
加載到 od 中運行程序,程序發現了我們啟動了 od,并打印出了 SOD 的驅動名
程序 main 函數在0x401000
處,在這里用了 EnumDeviceDrivers
和 GetDeviceDriverFileNameA
函數來枚舉系統中已經加載的驅動,并判斷 dbghelp.dll 路徑與系統路徑是否相同,如果不同 就認為系統中運行了 od 驅動對象的 DriverSection 保存了一個指向_LDR_DATA_TABLE_ENTRY 指針,這個結構中有著一 個所有驅動的鏈表,通過遍歷這個鏈表就可以知道加載了哪些驅動,如果想要程序找不到一 個驅動,我們只需要從這個鏈表中把相應的驅動摘下來 以下是具體代碼:
#include <ntddk.h>
#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE")
#define PROCESS_ID_OFFSET 0x84
#define IMAGE_NAME_OFFSET 0x174
#define PROCESS_LINK_OFFSET 0x88
#define PROCESS_EXITTIME 0x78
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID *ServiceTableBase;
PULONG *ServiceCounterTableBase;
ULONG NumberOfServices;
ULONG ParamTableBase;
}SSDT, *PSSDT;
__declspec(dllimport) SSDT KeServiceDescriptorTable;
//_LDR_DATA_TABLE_ENTRY 聲明
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union
{
LIST_ENTRY HashLinks;
struct
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
ULONG TimeDateStamp;
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
/////////////////////////////////////////////////////////////
void DriverUnload(IN PDRIVER_OBJECT pDriverObject);
void ShowProcess();
struct _LIST_ENTRY *tmp;
/////////////////////////////////////////////////////////////
#pragma INITCODE
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegisterPath)
{
PLDR_DATA_TABLE_ENTRY pTableEntry = NULL;
PLIST_ENTRY pCur = NULL, pHead = NULL;
UNICODE_STRING str_Name;
ULONG EProcess,FirstEProcess;
LIST_ENTRY* ActiveProcessLinks;
ULONG pid,dwCount=0;
PUCHAR pImage;
//PPROCESS_INFO ProcessInfo={0};
pDriverObject->DriverUnload = DriverUnload;
RtlInitUnicodeString(&str_Name, L"fengyue0.sys");
//從驅動對象中的 DriverSection 獲得_LDR_DATA_TABLE_ENTRY 指針
pTableEntry = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
//使頭指針和當前指針指向模塊鏈表中的一個模塊
pCur = pHead = pTableEntry->InLoadOrderLinks.Blink;
do
{
//獲得模塊指針
pTableEntry = (PLDR_DATA_TABLE_ENTRY)pCur;
if (pTableEntry->BaseDllName.Buffer)
{
if (RtlCompareUnicodeString(&str_Name, &(pTableEntry->BaseDllName), FALSE) ==
0)
{
//從鏈表中刪除模塊
pTableEntry->InLoadOrderLinks.Blink->Flink =
pTableEntry->InLoadOrderLinks.Flink;
pTableEntry->InLoadOrderLinks.Flink->Blink =
pTableEntry->InLoadOrderLinks.Blink;
break;
}
}
pCur = pCur->Flink;
}while (pCur != pHead);
/////////////////////////////////////////////////////////////
//顯示進程
ShowProcess();
return STATUS_SUCCESS;
}
#pragma PAGECODE
void DriverUnload(IN PDRIVER_OBJECT pDriverObject)
{
}
void ShowProcess()
{
ULONG OriFunAddr;
UNICODE_STRING str_FuncName;
PULONG ssdt_base;
RtlInitUnicodeString(&str_FuncName,L"NtQuerySystemInformation");
//獲取 ssdt 基址
ssdt_base = (PULONG) KeServiceDescriptorTable.ServiceTableBase;
//獲取 NtQuerySystemInformation 原地址
OriFunAddr =(ULONG) MmGetSystemRoutineAddress(&str_FuncName);
//判斷當前 ssdt 中地址是否被替換
if (ssdt_base[0xAD] != OriFunAddr)
{
DbgPrint("NtQuerySystemInformation has been hooked!fix it...");
ssdt_base[0xAD] = OriFunAddr;
DbgPrint("success!");
}
}
題目中的第二個目標就是使 od 進程在 windows 自帶的任務管理器中顯示出來, fengyue0.sys 驅動會在 ssdt 中把 NtQuerySystemInformation
函數 hook 掉,所以,當 windows 的任務管理 器通過這個函數枚舉進程的時候就會把 od 進程隱藏了
知道了隱藏的原理之后,我們的目標就轉換為了,把 NtQuerySystemInformation
的 ssdt hook 給恢復了。 在 KeServiceDescriptorTable
中可以獲得 ssdt 的基地址,再通過 NtQuerySystemInformation
在 ssdt 中的索引號就可以得到在 ssdt 中存放的它的地址,但是 ssdt 中的地址不一定是函數真 正的地址,有可能是被 hook 了的地址,所以可以再 MmGetSystemRoutineAddress 獲得函數 真正的地址,然后與 ssdt 中的對比,如果相同則說明函數沒有被 hook,反之函數被 hook, 我們的目的是恢復它的真正地址,現在既然已經有了真正的地址,就可以直接把 ssdt 中的 假地址給換掉,就恢復了 NtQuerySystemInformation
的 ssdt hook。具體代碼在上述代碼的 ShowProcess()函數中。
用 od 加 載 目 標 程 序 后 , 把 我 們 的 驅 動 用 DriverMonitor 加 載 并 運
之后在 od 中運行目標程序
程序顯示沒有發現 od,說明我們隱藏驅動成功了,再到 windows 任務管理器中查看一下, ollydbg.exe 進程也出現了