在二进制文件中的任何地方都找不到密码

逆向工程 拆卸 小精灵 海湾合作委员会
2021-06-12 08:25:37

这是我在此的头一篇博文。我最近参与了一个捕获标志准备测试,该测试涉及反编译一个 ELF 32 位 LSB 可执行文件,用 GCC 编译的用于 Linux 的 Intel 80386 文件。二进制文件应该包含提供的 zip 文件的密码。这只是为了准备将于 10 月 5 日举行的实际夺旗挑战。我能够解决其他挑战,但这似乎是不可能的。

我在那里使用 base64 上传了文件。

起初我不得不安装 GCC-multilib,因为我无法在 32 位系统上运行二进制文件。

我使用字符串、objdump 和radare2 来了解二进制文件中的内容。我最终发现 main 函数有一个奇怪的行为:它调用 strcompare 与输入字符串和“s3cR3t_p4sSw0rD”,但如果两个字符串匹配,它会打印出“这不是您正在寻找的解决方案 :)”,另一方面,如果它们不匹配,它会在你的输入上调用一个 stringlength 函数,如果它不是 34 个字符长,它会打印出"Try again :("

没有成功字符串,实际上在所有其他情况下程序都会停止。

我尝试使用radare debug 来运行代码,但如果我运行它,则会打印出“请不要使用调试器!” . 我最终找到了似乎发生此检查的指令,并添加了一条jmp指令来绕过检查。我无法添加任何断点并通过调试器运行程序。它只是结束而不打印任何内容。

有一些函数包含字符数据,但我没有发现任何有用的东西。

请帮我找到密码,我快被这个弄疯了。

Base 64 数据(注意,这是一个 zip 文件而不是一个 url...):这里

2个回答

你看错了。在运行 main 之前查看函数,你会找到解密函数。

当您使用解密功能时,您将获得存档的密钥: 1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!

和标志: {FLG:4#hfoU98Y5(ButYou'llNeverKnowIt)}

易经

part 1 = [2, 93, 2, 103, 108, 7, 93, 119, 108, 117, 2, 93, 2, 108, 7, 65, 97, 7, 106, 64, 108, 7, 65, 0, 108, 96, 3, 108, 0, 7, 64, 106, 18, 18, 0]     
for i in range(0, len(part_1) - 1):
      print(chr(part_1[i] ^ 0x33))

由于另一个问题基于相同的二进制文件,并且接受的答案没有详细说明如何找到负责的函数,这里有一篇文章。平常的东西$ r2 wysiNwyg; aaa

看看功能列表(afl)

[0x080484a0]> afl
0x080483bc    3 35           fcn.080483bc
0x080483f0    1 6            sym.imp.strcmp
0x08048400    1 6            sym.imp.printf
0x08048410    1 6            sym.imp.fgets
0x08048420    1 6            sym.imp.puts
0x08048430    1 6            loc.imp.__gmon_start
0x08048440    1 6            sym.imp.exit
0x08048450    1 6            sym.imp.strlen
0x08048460    1 6            sym.imp.__libc_start_main
0x08048470    1 6            sym.imp.memset
0x08048480    1 6            sym.imp.putchar
0x08048490    1 6            sym.imp.ptrace
0x080484a0    1 33           entry0
0x080484d0    1 4            fcn.080484d0
0x080484e0    4 43           fcn.080484e0
0x08048550    3 30           entry3.fini
0x08048570    8 43   -> 93   entry1.init
0x0804859b    3 55           entry2.init
0x080485d2   12 446          entry4.fini
0x08048790    8 250          main

除此之外mainentry4.fini功能相当大。查看二进制文件(iS)中的.fini.fini_array部分。.fini_array当程序即将终止时(在 main 之后)调用函数 from

[0x080484a0]> iS~fini
14 0x00000904    20 0x08048904    20 -r-x .fini
19 0x00000c08     8 0x08049c08     8 -rw- .fini_array

寻找那个地址。转储.fini_array(pxw)。

[0x080484a0]> s 0x08049c08
[0x08049c08]> pxw 0x10
0x08049c08  0x08048550 0x080485d2 0x00000000 0x00000001  P...............

entry4.fini中已被引用.fini_array转到主要和反汇编。检查输入的存储位置

│           0x080487d5      a1409d0408     mov eax, dword [obj.stdin]  ; [0x8049d40:4]=0
│           0x080487da      83ec04         sub esp, 4
│           0x080487dd      50             push eax                    ; FILE *stream
│           0x080487de      6a23           push 0x23                   ; '#' ; 35 ; int size
│           0x080487e0      68609d0408     push 0x8049d60              ; char *s
│           0x080487e5      e826fcffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)

来自 fgets 的输入将到达 0x8049d60。你也可以给它命名f input 35 @ 0x8049d60检查其上的外部参照

[0x08048790]> axt 0x8049d60
entry4.fini 0x80486ec [DATA] push 0x8049d60
main 0x80487c8 [DATA] push 0x8049d60
main 0x80487e0 [DATA] push 0x8049d60
main 0x80487f8 [DATA] push 0x8049d60
main 0x8048808 [DATA] movzx eax, byte [eax + 0x8049d60]
main 0x8048816 [DATA] push 0x8049d60
main 0x8048826 [DATA] mov byte [eax + 0x8049d60], 0
main 0x8048835 [DATA] push 0x8049d60
main 0x804885b [DATA] push 0x8049d60

entry4.fini还引用了输入。拆开它。

│           0x080486ec      68609d0408     push 0x8049d60              ; const char *s
│           0x080486f1      e85afdffff     call sym.imp.strlen         ; size_t strlen(const char *s)
│           0x080486f6      83c410         add esp, 0x10
│           0x080486f9      83f822         cmp eax, 0x22               ; '"' ; 34

首先检查您的输入是否为 34 字节长。

│           0x080486f9      83f822         cmp eax, 0x22               ; '"' ; 34
│       ┌─< 0x080486fc      7405           je 0x8048703
│      ┌──< 0x080486fe      e988000000     jmp 0x804878b
│      ││   ; CODE XREF from entry4.fini (0x80486fc)
│      │└─> 0x08048703      c745f4000000.  mov dword [local_ch], 0
│      │┌─< 0x0804870a      eb2c           jmp 0x8048738
│      ││   ; CODE XREF from entry4.fini (0x804873c)
│     ┌───> 0x0804870c      8d55d1         lea edx, [local_2fh]
│     ⁝││   0x0804870f      8b45f4         mov eax, dword [local_ch]
│     ⁝││   0x08048712      01d0           add eax, edx
│     ⁝││   0x08048714      0fb600         movzx eax, byte [eax]
│     ⁝││   0x08048717      0fbed0         movsx edx, al
│     ⁝││   0x0804871a      8b45f4         mov eax, dword [local_ch]
│     ⁝││   0x0804871d      05609d0408     add eax, 0x8049d60
│     ⁝││   0x08048722      0fb600         movzx eax, byte [eax]
│     ⁝││   0x08048725      83f033         xor eax, 0x33
│     ⁝││   0x08048728      0fbec0         movsx eax, al
│     ⁝││   0x0804872b      0fb6c0         movzx eax, al
│     ⁝││   0x0804872e      39c2           cmp edx, eax
│    ┌────< 0x08048730      7402           je 0x8048734
│   ┌─────< 0x08048732      eb57           jmp 0x804878b
│   ││⁝││   ; CODE XREF from entry4.fini (0x8048730)
│   │└────> 0x08048734      8345f401       add dword [local_ch], 1
│   │ ⁝││   ; CODE XREF from entry4.fini (0x804870a)
│   │ ⁝│└─> 0x08048738      837df421       cmp dword [local_ch], 0x21  ; [0x21:4]=-1 ; '!' ; 33
│   │ └───< 0x0804873c      7ece           jle 0x804870c
│   │  │    0x0804873e      c745f4000000.  mov dword [local_ch], 0
│   │  │┌─< 0x08048745      eb21           jmp 0x8048768

第二次检查涉及对每个字节local_2fh与 0x33进行异或,并逐字节与输入进行比较。启动 VM 转储内存(来自 local_2fh 的 0x22 字节)并执行异或操作。

[0x080484a0]> s entry4.fini
[0x080485d2]> aei
[0x080485d2]> aeim
[0x080485d2]> aeip

局部变量被分配到指令 0x08048665 以进行检查。模拟和转储值。

[0x080485d2]> aesu 0x08048665
[0x08048659]> pcp 0x22  @ebp-0x2f 
import struct
buf = struct.pack ("34B", *[
0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
0x12])
[0x00000041]> !python
Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> buf = struct.pack ("34B", *[
... 0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
... 0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
... 0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
... 0x12])
>>> print ''.join(map(lambda x:chr(ord(x)^0x33),buf))
1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!

这有效

./wysiNwyg 

#########################################################
### Welcome to the "wysiNwyg" challenge!
###     Your task is to find out the password to be able
###     to decrypt the password-protected zip and read
###     the secret flag. Good Luck!!
#########################################################

Password: 1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!
Congratulations! You just won :p