吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 20020|回复: 44
收起左侧

[原创] 吾爱破解2016安全挑战赛windows组题目分析

  [复制链接]
onesbug 发表于 2016-4-15 01:17
本帖最后由 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



免费评分

参与人数 20吾爱币 +1 热心值 +20 收起 理由
ikaros + 1 + 1 谢谢@Thanks!
q3125418 + 1 一等奖发红包否?
微若清风 + 1 热心回复!
codelive + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
lsj125090311 + 1 我很赞同!
冰魂king + 1 我很赞同!
lingyulingyu + 1 热心回复!
知足zz + 1 要是这都没技术含量,让我们情何以堪
苏紫方璇 + 1 谢谢@Thanks!
yypE + 1 谢谢@Thanks!
Lnairan + 1 谢谢@Thanks!
铅笔刀 + 1 大赛第一名 牛逼
286733081 + 1 膜拜!
1332988891abc + 1 我很赞同!
〇〇木一 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
gmh5225 + 1 我很赞同!
Terrorblade + 1 谢谢@Thanks!
我是你姐夫 + 1 谢谢@Thanks!
蚯蚓翔龙 + 1 谢谢@Thanks!
Sound + 1 已经处理,感谢您对吾爱破解论坛的支持!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Memory_code 发表于 2016-4-15 08:36
啊哈……第二题跟我做的一样……但是我在做的时候就已经知道不符合规定了……最后时间不多就提交了
事后翻WRK发现设置一下ControlArea->u.Flags.DeleteOnClose = 1可以满足三个要求,但是先开进程在开驱动就不行了……

最后在看雪上有,用File ID算是正统的完美解法……但是应该还有别的解法……期待大牛……
 楼主| onesbug 发表于 2016-4-18 22:29
冰魂king 发表于 2016-4-18 11:57
请教下大大,就是call eax进去就到了动态空间,然后把这个区段dump出来直接IDA分析,大大是如何把这个区段 ...

call eax进去后看这个地址在进程中的哪个内存段中,右键保存即可。DMP文件直接拖到IDA里去就行了。
LightSylcanus 发表于 2016-4-15 01:24
Sound 发表于 2016-4-15 02:37
bugheihei , {:1_931:}
蚯蚓翔龙 发表于 2016-4-15 07:34
cm完全没看出会跟魔方推箱子什么的有关...
头像被屏蔽
陈南煜 发表于 2016-4-15 08:18
很有技术含量~~~
wsxqaz13245 发表于 2016-4-15 08:59
楼主,不错不错
头像被屏蔽
轩辕之风 发表于 2016-4-15 09:32
全能大神,赞!
赫色的天空 发表于 2016-4-15 10:36
门外汉来凑个热闹啊
currwin 发表于 2016-4-15 12:44
盯----------------------
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 01:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表