好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 onesbug 于 2016-4-15 01:41 编辑
PS:我的答案没有其他几位选手的高精尖,请各位看客随便看看就行了
第一题CM:
这个CM里的按钮事件的代码粗看了下是加的花指令,猜想手动Step Over也多花不了几分钟,就没管这些花指令,然后找到加载自定义可执行数据的代码,call eax进去就到了动态空间,然后把这个区段dump出来直接IDA 分析。
下面是这个dump的分析:
先上结构:
struct face {
int x;
int y;
float* ptr;
face(){
x = 3;
y = 3;
ptr = new float[x][y];
}
};
x,y表示魔方某一个面上的坐标,ptr是二维数组,表示每个面上面的颜色
首先,根据用户名得到checksum % 1,这是一个奇偶位,
然后根据密码的前18字节做种子生成一个对应的魔方,但是这个魔方是否有效的关键就在用户名的奇偶位。
由于用户名计算只有两种结果,所以只要有key,随随便便就可以碰撞出一个不同的用户名出来。
只要生成的是一个有效魔方,第一个校验就绕过了。
下一步填充一个固定的未排序的9字节的数组
10013F10 07 01 04 00 03 08 02 05 06 .
还有一个初始索引=3
10013F10 03 00 00 00 ...
遍历用户名的每一字节值 % 4 = {0,1,2,3},有4种可能性,走对应的4种移动流程。
每种流程对当前索引值进行判断,如果不在条件范围内则忽略这个位上的用户名。
否则走以下4种
位置向左移动1,索引加1
位置向右移动1,索引减1
位置向左移动3, 索引加3
位置向右移动3,索引减3
索引改变后,如果索引小于<10,那么将移动前所在位置的值清0。
用户名遍历结束后,将生成对应的乱序数组。
下一步再计算密码的18位以后的字符串,这个字符串只有0-8这9个数字是有效的,其他的都会忽略。
那么可以根据这个生成无限多的KEY。
为了抓紧时间,我先随便碰两组不同的用户名和KEY出来:
sdss
323031303330213032145210F367D410345E87634103478
第二组
c
323031303330213032113452100236741034587634103478
这下用户名和KEY和标准的完全不一样了。
简单地说,就是拿用户名生成一个乱序9字节的数组+一个索引位,再用密码的18位之后的字符串去计算,将这个乱序数组最终恢复成012345678就通过校验。
PS:这是当时记的笔记,由于当时时间太紧没有仔细看,魔方那个是猜的,第二个验证当时也没看出是推箱子游戏,直接把重点放在了边界判断上面,所以只参杂了一些不会参与计算的数字进去,没什么技术含量,发出来都有点不好意思。
第六题CM:
这个cm基本不算技巧型的,而是实用性的,这个cm用了几个修改过的三方库:
1.用了一个C语言的健身库(jansson)来处理json数据(我头一个小时没key的时候都用在学这个库上了。。)
2.修改过码表的base64算法,并且每个字节异或一下。这个我扪头搞了将近2个小时,把代码都快逆完了,喝茶的时候,才反应过来这是base64。看来基本功太久不练是要废掉的啊。。
3.加密版本的lua。
首先,将key用非标准base64解码出杰森,然后取name的键值,判断和输入的name是否一样。
然后取key1,key2,key3。
key1是标准摩斯码,搞个在线解码工具,就能得到“7105042118043”,7是截断位,把数值分成两部分18043和1050421相乘得到18952746103,最后校验这个数字是否完全包含了0到9这10个数字。
key2是一个16进制数,这个key我没仔细看,因为key2和其他因子和用户名都不产生关系,可以直接使用这个key,所以我就不管了。
key3是个恶心的东西,key3是唯一一个和user产生关系的,也就是说这个数必须和用户名配对。整个cm的关键其实就在这个校验,而这个校验算法是在lua字节码里的(看图),保密工作做得挺好,连字符串都不给看。
ttt.png (45.26 KB, 下载次数: 0)
下载附件 保存到相册
2016-3-28 09:29 上传
这个搞了我好几个小时,后来太困了就睡了。好在主办方可怜我们,算法不是很复杂。
睡了几个小时早上起来后思路清晰了点,下内存断点分析了几次数值计算,根据经验来看感觉只是个简单的异或之类的,凭感觉在原用户名和key3的值上小范围的浮动,key3是可以碰撞到的,
并且base64加密后和原来的key可以做到完全不一样,这个已经达到题目的要求了。所以我就手动碰了几个出来
361
ZLIRqJpxgGYAqRIRqR39rojbkUZRqADAZf6ImUZIHo2xHU4RHU2CHo2RHo2CHo2RHU4xHU4RHo2CHo2RHU4xHU2RHU4CHo2RHU2CHo2RHU2CHo2RHo2CHU4RHo2CHo2RHU4xHU2RHU4xHo2RqU/ZqJZI6dHarUj/qJj8mfjLufkhrfvIHpIRqJp1XlxLqRIRqR6Brf6Brf6Brf6Brf6BrfjamfxaqPPn
370
ZLIRqJpxgGYAqRIRqR3ErJjbkUZRqADAZf6ImUZIHo2xHU4RHU2CHo2RHo2CHo2RHU4xHU4RHo2CHo2RHU4xHU2RHU4CHo2RHU2CHo2RHU2CHo2RHo2CHU4RHo2CHo2RHU4xHU2RHU4xHo2RqU/ZqJZI6dHarUj/qJj8mfjLufkhrfvIHpIRqJp1XlxLqRIRqRjBrf6Brf6Brf6Brf6Brf6amfxaqPPn
371
ZLIRqJpxgGYAqRIRqR3ErojbkUZRqADAZf6ImUZIHo2xHU4RHU2CHo2RHo2CHo2RHU4xHU4RHo2CHo2RHU4xHU2RHU4CHo2RHU2CHo2RHU2CHo2RHo2CHU4RHo2CHo2RHU4xHU2RHU4xHo2RqU/ZqJZI6dHarUj/qJj8mfjLufkhrfvIHpIRqJp1XlxLqRIRqR64rf6Brf6Brf6Brf6BrfjamfxaqPPn
这个答案也没什么技术含量,献丑了。。
第二题路径文件监视:
这个题的考点应该是路径的变化和文件名的变化,好像用某一种单纯的方法都不能完全达标(或许真的有这种单纯的一种方法就能实时获取的方法?会的人指教下)
我是用PEB获取模块的实时路径(不要文件名),再获取EPROCESS里的sectionobject得到FilePointer获取实时文件名,然后将两个路径组合起来,相关代码是这样的:
//是否是vista之后的版本
bool vistaGreater(){
static bool isInit = false;
static RTL_OSVERSIONINFOW osVersion = { 0 };
if (!isInit){
RtlGetVersion(&osVersion);
isInit = true;
}
if (osVersion.dwMajorVersion >= 6)
return true;
return false;
}
//BlackBone
/// <summary>
/// Get file name from full path
/// </summary>
/// <param name="path">Path.</param>
/// <param name="name">Resulting name</param>
/// <returns>Status code</returns>
NTSTATUS BBStripPath(IN PUNICODE_STRING path, OUT PUNICODE_STRING name)
{
ASSERT(path != NULL && name);
if (path == NULL || name == NULL)
return STATUS_INVALID_PARAMETER;
// Empty string
if (path->Length < 2)
{
*name = *path;
return STATUS_NOT_FOUND;
}
for (USHORT i = (path->Length / sizeof(WCHAR)) - 1; i != 0; i--)
{
if (path->Buffer == L'\\' || path->Buffer == L'/')
{
name->Buffer = &path->Buffer[i + 1];
name->Length = name->MaximumLength = path->Length - (i + 1)*sizeof(WCHAR);
return STATUS_SUCCESS;
}
}
*name = *path;
return STATUS_NOT_FOUND;
}
/// <summary>
/// Get directory path name from full path
/// </summary>
/// <param name="path">Path</param>
/// <param name="name">Resulting directory path</param>
/// <returns>Status code</returns>
NTSTATUS BBStripFilename(IN PUNICODE_STRING path, OUT PUNICODE_STRING dir)
{
ASSERT(path != NULL && dir);
if (path == NULL || dir == NULL)
return STATUS_INVALID_PARAMETER;
// Empty string
if (path->Length < 2)
{
*dir = *path;
return STATUS_NOT_FOUND;
}
for (USHORT i = (path->Length / sizeof(WCHAR)) - 1; i != 0; i--)
{
if (path->Buffer == L'\\' || path->Buffer == L'/')
{
dir->Buffer = path->Buffer;
dir->Length = dir->MaximumLength = i*sizeof(WCHAR);
return STATUS_SUCCESS;
}
}
*dir = *path;
return STATUS_NOT_FOUND;
}
//定位SectionObject在EPROCESS中的偏移
BOOL getSectionObjectOffsetInEProcess(PEPROCESS eprocess,ULONG& Offset){
/*
ntdll!_EPROCESS
...
+0x268 SectionObject : Ptr64 Void
+0x270 SectionBaseAddress : Ptr64 Void
...
+0x4d8 CreateUnbiasedInterruptTime : Uint8B
*/
///由于SectionObject在EPROCESS中的位置不一样,为了兼容性XP和WIN7(WIN10没试)
///在EPROCESS中搜索SectionBaseAddress来定位到SectionObject
Offset = 0;
PVOID sectionBaseAddress;
sectionBaseAddress = PsGetProcessSectionBaseAddress(eprocess);
if (!sectionBaseAddress)
return FALSE;
//从EPROCESS搜索0x127个指针,应该够了,
for (int i = 0; i < sizeof(PVOID) * 0x127; i++){
if (MmIsAddressValid((PVOID)((PBYTE)eprocess + i))){
if (*(PVOID*)((PCHAR)eprocess + i) == sectionBaseAddress){
//SectionBaseAddress的前一个就是SectionObject
Offset = i - sizeof(ULONG_PTR);
return TRUE;
}
}
}
return TRUE;
}
bool initilization(){
UNICODE_STRING procName;
RtlInitUnicodeString(&procName, L"PsGetProcessSectionBaseAddress");
PsGetProcessSectionBaseAddress = (TPsGetProcessSectionBaseAddress)MmGetSystemRoutineAddress(&procName);
if (!PsGetProcessSectionBaseAddress){
DbgPrint("Could not find PsGetProcessSectionBaseAddress\n");
RtlFreeUnicodeString(&procName);
return false;
}
RtlInitUnicodeString(&procName, L"PsGetProcessPeb");
PsGetProcessPeb = (TsGetProcessPeb)MmGetSystemRoutineAddress(&procName);
if (PsGetProcessPeb == NULL){
DbgPrint("Could not find PsGetProcessPeb\n");
return FALSE;
}
return true;
}
ULONG SecObj_Offset = 0;
NTSTATUS PrivatePsReferenceProcessFilePointer(IN PEPROCESS EProcess, OUT PVOID *OutFileObject){
//第一次调用先定位SectionObject在EPROCESS中的偏移
if (!EProcess)
return STATUS_UNSUCCESSFUL;
if (!SecObj_Offset){
if (!getSectionObjectOffsetInEProcess(EProcess, SecObj_Offset)){
//DbgPrint("locate SectionObject in EPROCESS Error!!!\n");
return STATUS_UNSUCCESSFUL;
}
}
//DbgPrint("SectionObject in EPROCESS = %08X\n", SecObj_Offset);
SECTION_OBJECT* sec_obj = *(SECTION_OBJECT**)((PBYTE)EProcess + SecObj_Offset);
if (!sec_obj)
return STATUS_UNSUCCESSFUL;
if (!sec_obj->SegmentObject || !sec_obj->SegmentObject->ControlArea || !sec_obj->SegmentObject->ControlArea->FilePointer)
return STATUS_UNSUCCESSFUL;
PVOID object = sec_obj->SegmentObject->ControlArea->FilePointer;
if (vistaGreater())
object = (PVOID)((ULONG_PTR)object & ((ULONG_PTR)~0x000F));
NTSTATUS status = ObReferenceObjectByPointer(object, GENERIC_READ, *IoFileObjectType, KernelMode);
if (NT_SUCCESS(status))
*OutFileObject = object;
return status;
}
BOOL getProcessFilePathbyFileObject(PEPROCESS Process, wchar_t *buffer, int bufferSize){
NTSTATUS status;
BOOL result = FALSE;
ULONG uReturnLength = 0;
PVOID pv = NULL;
PFILE_OBJECT procFileObject = NULL;
status = PrivatePsReferenceProcessFilePointer(Process, (void**)&procFileObject);
if (!NT_SUCCESS(status)){
//DbgPrint("Get FileObject Error:0x%08X\n", status);
if (procFileObject)
ObfDereferenceObject(procFileObject);
return FALSE;
}
PUNICODE_STRING pFullPath = (PUNICODE_STRING)ExAllocatePoolWithTag(NonPagedPool, 1024 + sizeof(UNICODE_STRING), 'TAG0');
if (!pFullPath){
if (procFileObject)
ObfDereferenceObject(procFileObject);
return FALSE;
}
status = ObQueryNameString(procFileObject, (POBJECT_NAME_INFORMATION)pFullPath, 1024 + sizeof(UNICODE_STRING), &uReturnLength);
if (!NT_SUCCESS(status)){
//DbgPrint("ObQueryNameString Error = 0x%08X \n", status);
if (procFileObject)
ObfDereferenceObject(procFileObject);
return FALSE;
}
if (pFullPath->Buffer && pFullPath->Buffer[0] && pFullPath->Length > 0){
RtlStringCchCopyW(buffer, bufferSize, pFullPath->Buffer);
ExFreePoolWithTag(pFullPath, 'TAG0');
return TRUE;
}
ExFreePoolWithTag(pFullPath, 'TAG0');
return FALSE;
}
//从PEB中取模块路径
BOOL getProcessFilePathbyPEB(PEPROCESS Process, wchar_t *buffer, int bufsize){
PPEB peb = NULL;
peb = (PEB*)PsGetProcessPeb(Process);
if (!peb) return FALSE;
KAPC_STATE apcState = { 0 };
KeStackAttachProcess(Process, &apcState);
if (!peb->Ldr){
KeUnstackDetachProcess(&apcState);
return FALSE;
}
PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(peb->Ldr->InMemoryOrderModuleList.Flink,
LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (!entry){
KeUnstackDetachProcess(&apcState);
return FALSE;
}
PUNICODE_STRING fullImageFileName;
fullImageFileName = (PUNICODE_STRING)&entry->FullDllName;
if (fullImageFileName->Buffer != NULL &&
fullImageFileName->Buffer[0] != 0 &&
fullImageFileName->Length > 0){
RtlStringCchCopyW(buffer, bufsize, fullImageFileName->Buffer);
}
KeUnstackDetachProcess(&apcState);
return TRUE;
}
/// 这里说一下思路,获取模块路径用了EProcess->SectionObject得到FilePointer,另一种是读PEB中的模块路径
/// 这里不管使用哪一种都不能完全满足题目的3个要求,那么就只有将两个路径组合起来才能得到实时的路径
/// 代码也是东拼西凑的,勉强能满足题目的三个要求。
BOOL getprocessfilename(ULONG pid, wchar_t *buffer, int bufferSize){
PEPROCESS eprocess = NULL;
UNICODE_STRING SectionPath, PEBPath;
UNICODE_STRING Path, FileName;
wchar_t szSectionPath[MAX_PATH] = L"";
wchar_t szPEBPath[MAX_PATH] = L"";
if (!pid)
return FALSE;
if (!NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pid, &eprocess)))
return FALSE;
if (!getProcessFilePathbyFileObject(eprocess, szSectionPath, sizeof(szSectionPath) / sizeof(wchar_t))){
//DbgPrint("getProcessFilePathbyFileObject err\n");
ObDereferenceObject(eprocess);
return FALSE;
}
if (!getProcessFilePathbyPEB(eprocess, szPEBPath, sizeof(szPEBPath) / sizeof(wchar_t))){
//DbgPrint("getProcessFilePathbyPEB failure\n");
ObDereferenceObject(eprocess);
return FALSE;
}
ObDereferenceObject(eprocess);
RtlInitUnicodeString(&SectionPath, szSectionPath);
RtlInitUnicodeString(&PEBPath, szPEBPath);
if (BBStripPath(&SectionPath, &FileName) != STATUS_NOT_FOUND &&
BBStripFilename(&PEBPath, &Path) != STATUS_NOT_FOUND){
RtlStringCchPrintfW(buffer, bufferSize, L"%wZ\\%wZ", Path, FileName);//组合起来
return TRUE;
}
return FALSE;
}
//遍历进程
template<typename Func>
BOOL enumProcess(Func func){
ULONG retLength = 0;
PSYSTEM_PROCESSES proc;
NTSTATUS status = ZwQuerySystemInformation(SystemProcessInformation, NULL, 0, &retLength);
if (status != STATUS_INFO_LENGTH_MISMATCH)
return FALSE;
proc = (PSYSTEM_PROCESSES)ExAllocatePool(NonPagedPool, retLength);
if (proc == NULL){
return FALSE;
}
status = ZwQuerySystemInformation(SystemProcessInformation, proc, retLength, &retLength);
if (status != STATUS_SUCCESS){
ExFreePool(proc);
return FALSE;
}
PSYSTEM_PROCESSES curproc = (PSYSTEM_PROCESSES)proc;
bool over = false;
do{
if (GameOver)
break;
if (curproc->NextEntryDelta == 0)
over = true;
if (curproc->ProcessId > 0)
func(curproc->ProcessId);
curproc = (PSYSTEM_PROCESSES)((ULONG_PTR)curproc + curproc->NextEntryDelta);
} while (!over);
ExFreePool(proc);
return TRUE;
}
void workthread(void){
//不停遍历进程...
while (true){
if (GameOver)
break;
enumProcess([](HANDLE pid){
UNICODE_STRING procname = { 0 };
UNICODE_STRING dosProcessName = { 0 };
wchar_t processNameBuf[MAX_PATH] = L"";
wchar_t dosProcessNameBuf[MAX_PATH] = L"";
RtlInitEmptyUnicodeString(&dosProcessName, dosProcessNameBuf, sizeof(dosProcessNameBuf));
//取文件完全路径
if (!getprocessfilename((ULONG)pid, processNameBuf, sizeof(processNameBuf) / sizeof(wchar_t)))
return;
RtlInitUnicodeString(&procname, processNameBuf);
//DbgPrint("[procname]pid=%d,%wZ\n", (ULONG)pid, &procname);
NTSTATUS status = RtlUnicodeStringCopy(&dosProcessName, &procname);
if (!NT_SUCCESS(status))
return;
//是否在监视目录下
if (_wcsnicmp(dosProcessNameBuf, CheckDir,
sizeof(CheckDir) / sizeof(wchar_t) - 1) == 0){
DbgPrint("[control]pid=%d,%wZ\n", (ULONG)pid, &dosProcessName);
}
});
LARGE_INTEGER wait;
wait.QuadPart = -1000;
wait.QuadPart *= 10000;
KeDelayExecutionThread(KernelMode, FALSE, &wait);
if (GameOver)
break;
}
KeSetEvent(&stopEvent_, 0, FALSE);
PsTerminateSystemThread(0);
}
大概就是这样,这个题没拿满分,估计不是最优答案,看看就是了。。
第六题APC注入IE:
这个题是从Blackbone里扣出来的APC注入代码,没啥好说的,有兴趣的请看blackbone的源代码。
BTW本来也想做安卓和溢出的,比赛前环境放出来的时候已经google到是CVE-2015-3636了,因为只有这个洞符合放出来的环境,但在架环境和调试代码上浪费了些时间,周末时间太少,平常又在上班就没继续搞了。。没错,我就是马后炮
谢谢观看,浪费大家时间了。
http://bughoho.me
免费评分
查看全部评分