首页
社区
课程
招聘
[原创]看雪.京东 2018CTF-第三题分析(第一次做PWN)
发表于: 2018-6-22 09:10 7962

[原创]看雪.京东 2018CTF-第三题分析(第一次做PWN)

2018-6-22 09:10
7962
         
          (感觉我做的很复杂,可能不是出题者的本意)
          对于pwn,不知道啥意思,百度一下,大概的意思是:
1 、程序运行在服务器端,并构建一个类似tomcat的服务,并将服务的IP和端口提供出来,用户使用telnet等直接连接这个IP和端口后服务器就启动这个PWN进     程,从而进行交互。
2、运行在PWN的程序存在漏洞,客户端使用和服务交互的数据,来利用这个漏洞,从而来拿到服务器的shell。
3、flag已文件存储在服务器的系统上,通过ls来查找falg.txt,从而拿到flag。
  
          一、安装环境
          1、题目给出是服务器运行的环境是 ubuntu16.04 4.4.0-91-generic,在网上没找到对应的版本,直接下了16.04的版本安装。
                大版本相同,类似libc.so这样系统库,应该不会变化。因此此时认为我的系统的libc.so与服务器的libc.so是一致的。
           2、安装pwntools工具
               好像做PWN,大家都用这个工具,之前没用过,先安装再说。直接运行如下命令:
1 、程序运行在服务器端,并构建一个类似tomcat的服务,并将服务的IP和端口提供出来,用户使用telnet等直接连接这个IP和端口后服务器就启动这个PWN进     程,从而进行交互。
2、运行在PWN的程序存在漏洞,客户端使用和服务交互的数据,来利用这个漏洞,从而来拿到服务器的shell。
3、flag已文件存储在服务器的系统上,通过ls来查找falg.txt,从而拿到flag。
  
          一、安装环境
          1、题目给出是服务器运行的环境是 ubuntu16.04 4.4.0-91-generic,在网上没找到对应的版本,直接下了16.04的版本安装。
                大版本相同,类似libc.so这样系统库,应该不会变化。因此此时认为我的系统的libc.so与服务器的libc.so是一致的。
           2、安装pwntools工具
               好像做PWN,大家都用这个工具,之前没用过,先安装再说。直接运行如下命令:
               sudo pip install pwntools
               sudo pip install pwntools
     如果没有安装pip,需要安装下pip
               sudo apt-get install python-pip
     对于安装 pwntools如果pip的版本太低中间会出错,因此需要将pip升级到最高版本。执行如下命令:
                         pip install --upgrade pip
               直接在命令行输入:
                        checksec 后出现如下提示就表示pwntools安装成功了。
oooaooo@oooaooo-virtual-machine:~$ checksec
usage: pwn checksec [-h] [--file [elf [elf ...]]] [elf [elf ...]]
oooaooo@oooaooo-virtual-machine:~$ 
                         pip install --upgrade pip
               直接在命令行输入:
                        checksec 后出现如下提示就表示pwntools安装成功了。
oooaooo@oooaooo-virtual-machine:~$ checksec
usage: pwn checksec [-h] [--file [elf [elf ...]]] [elf [elf ...]]
oooaooo@oooaooo-virtual-machine:~$ 
oooaooo@oooaooo-virtual-machine:~$ checksec
usage: pwn checksec [-h] [--file [elf [elf ...]]] [elf [elf ...]]
oooaooo@oooaooo-virtual-machine:~$ 
二、IDA远程调试liunx(本程序是64位的使用ida64.exe调试)
二、IDA远程调试liunx(本程序是64位的使用ida64.exe调试)
二、IDA远程调试liunx(本程序是64位的使用ida64.exe调试)
           1、将IDA目录的linux_server64文件拷贝到ubunto中,并使其运行。
           2、打开IDA,选择"debuger->run->remote linux debugger"
           3、配置如下:
          

           点击OK就可以了,注意ida64要以管理员权限运行。
         
            三、WOW程序分析
            先不忙调试,看看程序架构 IDA载入后 如下:main函数如下:

         
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi

  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  welcome(v3, 0LL);
  JUMPOUT(__CS__, 4196376LL);
}
           1、将IDA目录的linux_server64文件拷贝到ubunto中,并使其运行。
           2、打开IDA,选择"debuger->run->remote linux debugger"
           3、配置如下:
          
           1、将IDA目录的linux_server64文件拷贝到ubunto中,并使其运行。
           2、打开IDA,选择"debuger->run->remote linux debugger"
           3、配置如下:
          

           点击OK就可以了,注意ida64要以管理员权限运行。
         
            三、WOW程序分析
            先不忙调试,看看程序架构 IDA载入后 如下:main函数如下:

         
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi

  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  welcome(v3, 0LL);
  JUMPOUT(__CS__, 4196376LL);
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi

  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  welcome(v3, 0LL);
  JUMPOUT(__CS__, 4196376LL);
}


从中可以看出最后一条语句,IDA 分析失败了:JUMPOUT(__CS__, 4196376LL) 先不管,看下welcome函数


从中可以看出最后一条语句,IDA 分析失败了:JUMPOUT(__CS__, 4196376LL) 先不管,看下welcome函数
1、welcome函数(过反调试)

__int64 welcome()
{
  puts("***************************\n");
  puts("2018 kanxue CTF\n");
  puts("***************************\n");
  puts("***************************\n");
  puts(&byte_400B78);
  puts(&byte_400BA8);
  puts(&byte_400BD8);
  puts(asc_400C01);
  puts("***************************\n");
  return test("***************************\n");
}

__int64 welcome()
{
  puts("***************************\n");
  puts("2018 kanxue CTF\n");
  puts("***************************\n");
  puts("***************************\n");
  puts(&byte_400B78);
  puts(&byte_400BA8);
  puts(&byte_400BD8);
  puts(asc_400C01);
  puts("***************************\n");
  return test("***************************\n");
}

是往终端上输出字符(输出了一首诗)
这里面需要注意的test函数。
text:0000000000400751
.text:0000000000400751                 public test
.text:0000000000400751 test            proc near               ; CODE XREF: welcome+63↑p
.text:0000000000400751 ; __unwind {
.text:0000000000400751                 push    rbp
.text:0000000000400752                 mov     rbp, rsp
.text:0000000000400755                 mov     rax, 65h
.text:000000000040075C                 mov     rcx, 0
.text:0000000000400763                 mov     rdx, 1          ; addr
.text:000000000040076A                 mov     rsi, 0          ; pid
.text:0000000000400771                 mov     rdi, 0          ; request
.text:0000000000400778                 syscall                 ; LINUX - sys_ptrace
.text:000000000040077A                 cmp     rax, 0
.text:000000000040077E                 jge     short L_K1
.text:0000000000400780                 mov     rax, 3Ch
.text:0000000000400787                 mov     rdi, 0          ; error_code
.text:000000000040078E                 syscall                 ; LINUX - sys_exit
.text:0000000000400790
.text:0000000000400790 L_K1:                                   ; CODE XREF: test+2D↑j
.text:0000000000400790                 nop
.text:0000000000400791                 nop
.text:0000000000400792                 pop     rbp
.text:0000000000400793                 retn
.text:0000000000400793 ; } // starts at 400751
.text:0000000000400793 test            endp
这个函数实际上是个反调试函数。liunx程序如果要调试,必须要使用ptrace函数来实现,包括IDA等。程序先ptrace自己,此时IDA将无法附加调试。
text:0000000000400751
.text:0000000000400751                 public test
.text:0000000000400751 test            proc near               ; CODE XREF: welcome+63↑p
.text:0000000000400751 ; __unwind {
.text:0000000000400751                 push    rbp
.text:0000000000400752                 mov     rbp, rsp
.text:0000000000400755                 mov     rax, 65h
.text:000000000040075C                 mov     rcx, 0
.text:0000000000400763                 mov     rdx, 1          ; addr
.text:000000000040076A                 mov     rsi, 0          ; pid
.text:0000000000400771                 mov     rdi, 0          ; request
.text:0000000000400778                 syscall                 ; LINUX - sys_ptrace
.text:000000000040077A                 cmp     rax, 0
.text:000000000040077E                 jge     short L_K1
.text:0000000000400780                 mov     rax, 3Ch
.text:0000000000400787                 mov     rdi, 0          ; error_code
.text:000000000040078E                 syscall                 ; LINUX - sys_exit
.text:0000000000400790
.text:0000000000400790 L_K1:                                   ; CODE XREF: test+2D↑j
.text:0000000000400790                 nop
.text:0000000000400791                 nop
.text:0000000000400792                 pop     rbp
.text:0000000000400793                 retn
.text:0000000000400793 ; } // starts at 400751
.text:0000000000400793 test            endp
这个函数实际上是个反调试函数。liunx程序如果要调试,必须要使用ptrace函数来实现,包括IDA等。程序先ptrace自己,此时IDA将无法附加调试。
如果在IDA已经调试程序的状态下,吱声跳跃ptrace,那么返回值是失败的,此时程序调用exit来结束自身。

因此在IDA运行到这里后直接将相关代码nop掉就可以过反调试了。

2、花指令
程序加入了一些花指令,使IDA分析失效,主要形式是:
.text:000000000040080C E8 00 00 00 00                                                  call    $+5
.text:0000000000400811
.text:0000000000400811                                                 L_64:
.text:0000000000400811 58                                                              pop     rax
.text:0000000000400812 48 83 C0 07                                                     add     rax, 7
.text:0000000000400816 FF E0                                                           jmp     rax
可以使用脚本直接将上面的代码nop掉就可以过掉花指令,由于时间比较紧,就先没做这部分。
.text:000000000040080C E8 00 00 00 00                                                  call    $+5
.text:0000000000400811
.text:0000000000400811                                                 L_64:
.text:0000000000400811 58                                                              pop     rax
.text:0000000000400812 48 83 C0 07                                                     add     rax, 7
.text:0000000000400816 FF E0                                                           jmp     rax
可以使用脚本直接将上面的代码nop掉就可以过掉花指令,由于时间比较紧,就先没做这部分。
3、SMC 
下面是对main函数后面代码分析
text:00000000004007E9                 call    _setbuf
.text:00000000004007EE                 mov     rax, cs:stdout@@GLIBC_2_2_5
.text:00000000004007F5                 mov     esi, 0          ; buf
.text:00000000004007FA                 mov     rdi, rax        ; stream
.text:00000000004007FD                 call    _setbuf
.text:0000000000400802                 mov     eax, 0
.text:0000000000400807                 call    welcome
//花指令
.text:000000000040080C                 call    $+5
.text:0000000000400811
.text:0000000000400811 L_64:
.text:0000000000400811                 pop     rax
.text:0000000000400812                 add     rax, 7
.text:0000000000400816                 jmp     rax
.text:0000000000400816 main            endp
.text:0000000000400816
.text:0000000000400818 ; ---------------------------------------------------------------------------

//调用mprotect将0x400000 大小0x1000属性改成可读可写可执行
.text:0000000000400818                 sar     rax, 0Ch
.text:000000000040081C                 shl     rax, 0Ch
.text:0000000000400820                 mov     rdi, rax
.text:0000000000400823                 mov     rdx, 7
.text:000000000040082A                 mov     rax, 0Ah
.text:0000000000400831                 mov     rsi, 1000h
.text:0000000000400838                 syscall                 ; LINUX - sys_mprotect

//从终端读取6个字符到全局变量szCh中
.text:000000000040083A                 xor     rax, rax
.text:000000000040083D                 mov     rdx, 6
.text:0000000000400844                 push    rax
.text:0000000000400845                 lea     rax, szCh
.text:000000000040084D                 mov     rsi, rax
.text:0000000000400850                 pop     rax
.text:0000000000400851                 mov     rdi, rax
.text:0000000000400854                 syscall                 ; LINUX -

//使用输入的第一个字符,与40087F地址开始进行亦或,并写回到40087F相关内容中
//直到遇到亦或的结果为0x90结束。
.text:0000000000400856                 call    $+5
.text:000000000040085B
.text:000000000040085B L0_64:
.text:000000000040085B                 pop     rax
.text:000000000040085C                 add     rax, 24h
.text:0000000000400860                 xor     rcx, rcx
.text:0000000000400863                 mov     dl, [rsi+rcx]
.text:0000000000400866
.text:0000000000400866 L1_64:                                  ; CODE XREF: .text:0000000000400878↓j
.text:0000000000400866                                         ; .text:000000000040087D↓j
.text:0000000000400866                 mov     bl, [rax+rcx]
.text:0000000000400869                 xor     bl, dl
.text:000000000040086B                 mov     [rax+rcx], bl
.text:000000000040086E                 mov     dh, [rax+rcx-1]
.text:0000000000400872                 inc     rcx
.text:0000000000400875                 cmp     dh, 0FBh
.text:0000000000400878                 jz      short L1_64
.text:000000000040087A                 cmp     bl, 90h
.text:000000000040087D                 jnz     short L1_64
.text:000000000040087F                 sub     eax, 2D6D61E8h
.text:0000000000400884                 db      48h
.text:0000000000400884                 in      rax, 65h
.text:0000000000400884 ; ---------------------------------------------------------------------------
.text:0000000000400887                 db  65h ; e
.text:0000000000400888                 db 65h, 2Dh, 54h, 0ACh
.text:000000000040088C LEn0_64         dd 556D79EFh
.text:0000000000400890                 dq 2DA49A2D6D79EDB6h, 0E5602D8A19459CE6h, 71EFAC542D656565h
1)程序调用mprotect,让代码段0x400000 开始大小为0x1000可读可写可执行。(这里面就存在一个漏洞,代码可以写了)
text:00000000004007E9                 call    _setbuf
.text:00000000004007EE                 mov     rax, cs:stdout@@GLIBC_2_2_5
.text:00000000004007F5                 mov     esi, 0          ; buf
.text:00000000004007FA                 mov     rdi, rax        ; stream
.text:00000000004007FD                 call    _setbuf
.text:0000000000400802                 mov     eax, 0
.text:0000000000400807                 call    welcome
//花指令
.text:000000000040080C                 call    $+5
.text:0000000000400811
.text:0000000000400811 L_64:
.text:0000000000400811                 pop     rax
.text:0000000000400812                 add     rax, 7
.text:0000000000400816                 jmp     rax
.text:0000000000400816 main            endp
.text:0000000000400816
.text:0000000000400818 ; ---------------------------------------------------------------------------

//调用mprotect将0x400000 大小0x1000属性改成可读可写可执行
.text:0000000000400818                 sar     rax, 0Ch
.text:000000000040081C                 shl     rax, 0Ch
.text:0000000000400820                 mov     rdi, rax
.text:0000000000400823                 mov     rdx, 7
.text:000000000040082A                 mov     rax, 0Ah
.text:0000000000400831                 mov     rsi, 1000h
.text:0000000000400838                 syscall                 ; LINUX - sys_mprotect

//从终端读取6个字符到全局变量szCh中
.text:000000000040083A                 xor     rax, rax
.text:000000000040083D                 mov     rdx, 6
.text:0000000000400844                 push    rax
.text:0000000000400845                 lea     rax, szCh
.text:000000000040084D                 mov     rsi, rax
.text:0000000000400850                 pop     rax
.text:0000000000400851                 mov     rdi, rax
.text:0000000000400854                 syscall                 ; LINUX -

//使用输入的第一个字符,与40087F地址开始进行亦或,并写回到40087F相关内容中
//直到遇到亦或的结果为0x90结束。
.text:0000000000400856                 call    $+5
.text:000000000040085B
.text:000000000040085B L0_64:
.text:000000000040085B                 pop     rax
.text:000000000040085C                 add     rax, 24h
.text:0000000000400860                 xor     rcx, rcx
.text:0000000000400863                 mov     dl, [rsi+rcx]
.text:0000000000400866
.text:0000000000400866 L1_64:                                  ; CODE XREF: .text:0000000000400878↓j
.text:0000000000400866                                         ; .text:000000000040087D↓j
.text:0000000000400866                 mov     bl, [rax+rcx]
.text:0000000000400869                 xor     bl, dl
.text:000000000040086B                 mov     [rax+rcx], bl
.text:000000000040086E                 mov     dh, [rax+rcx-1]
.text:0000000000400872                 inc     rcx
.text:0000000000400875                 cmp     dh, 0FBh
.text:0000000000400878                 jz      short L1_64
.text:000000000040087A                 cmp     bl, 90h
.text:000000000040087D                 jnz     short L1_64
.text:000000000040087F                 sub     eax, 2D6D61E8h
.text:0000000000400884                 db      48h
.text:0000000000400884                 in      rax, 65h
.text:0000000000400884 ; ---------------------------------------------------------------------------
.text:0000000000400887                 db  65h ; e
.text:0000000000400888                 db 65h, 2Dh, 54h, 0ACh
.text:000000000040088C LEn0_64         dd 556D79EFh
.text:0000000000400890                 dq 2DA49A2D6D79EDB6h, 0E5602D8A19459CE6h, 71EFAC542D656565h
1)程序调用mprotect,让代码段0x400000 开始大小为0x1000可读可写可执行。(这里面就存在一个漏洞,代码可以写了)
2)从终端读取6个字符到scZh全局变量中。
3)使用scZh的第一个字符对代码段40087F开始进行亦或解密。
4)解密终止条件是遇到亦或后的数据为0x90(nop指令)
从上面分析来看程序存在SMC,算法为亦或算法,秘钥为用户输入。这里就要猜测秘钥是什么,由于是亦或算法,比较好猜测,我们知道正常的汇编指令都存在类似如下的指令:
.text:0000000000400823 48 C7 C2 07 00 00 00                                            mov     rdx, 7
.text:000000000040082A 48 C7 C0 0A 00 00 00                                            mov     rax, 0Ah
.text:0000000000400831 48 C7 C6 00 10 00 00                                            mov     rsi, 1000h
.text:0000000000400823 48 C7 C2 07 00 00 00                                            mov     rdx, 7
.text:000000000040082A 48 C7 C0 0A 00 00 00                                            mov     rax, 0Ah
.text:0000000000400831 48 C7 C6 00 10 00 00                                            mov     rsi, 1000h
 
多个0连续挨着,而任何数和0亦或就为它本身,因此我们在加密后的数据中查找连续一样的数据,就可以猜到秘钥。如下:
.text:000000000040087F 2D                                                              db 2Dh
.text:0000000000400880 E8                                                              db 0E8h
.text:0000000000400881 61                                                              db  61h ; a
.text:0000000000400882 6D                                                              db  6Dh ; m
.text:0000000000400883 2D                                                              db  2Dh ; -
.text:0000000000400884 48                                                              db 48h
.text:0000000000400885 E5                                                              db 0E5h
.text:0000000000400886 65                                                              db  65h ; e
.text:0000000000400887 65                                                              db  65h ; e
.text:0000000000400888 65                                                              db 65h
.text:0000000000400889 2D                                                              db  2Dh ; -

.text:000000000040087F 2D                                                              db 2Dh
.text:0000000000400880 E8                                                              db 0E8h
.text:0000000000400881 61                                                              db  61h ; a
.text:0000000000400882 6D                                                              db  6Dh ; m
.text:0000000000400883 2D                                                              db  2Dh ; -
.text:0000000000400884 48                                                              db 48h
.text:0000000000400885 E5                                                              db 0E5h
.text:0000000000400886 65                                                              db  65h ; e
.text:0000000000400887 65                                                              db  65h ; e
.text:0000000000400888 65                                                              db 65h
.text:0000000000400889 2D                                                              db  2Dh ; -

看到40886开始有3个连续的0x65,因此我们可以断定第一秘钥为0x65,我们IDA载入输入'eAAAAA'后执行解密代码后,如下:
.text:000000000040087F 48 8D 04 08                                     lea     rax, [rax+rcx]
.text:0000000000400883 48 2D 80 00 00 00                               sub     rax, 80h
.text:0000000000400889 48 31 C9                                        xor     rcx, rcx
.text:000000000040088C
.text:000000000040088C                                                 LEn0_64:                                ; CODE XREF: .text:000000000040089B↓j
.text:000000000040088C 8A 1C 08                                        mov     bl, [rax+rcx]
.text:000000000040088F 30 D3                                           xor     bl, dl
.text:0000000000400891 88 1C 08                                        mov     [rax+rcx], bl
.text:0000000000400894 48 FF C1                                        inc     rcx
.text:0000000000400897 48 83 F9 20                                     cmp     rcx, 20h
.text:000000000040089B 7C EF                                           jl      short LEn0_64
.text:000000000040089D 48 05 80 00 00 00                               add     rax, 80h
.text:00000000004008A3 48 31 C9                                        xor     rcx, rcx
.text:00000000004008A6 8A 14 0E                                        mov     dl, [rsi+rcx]
.text:00000000004008A9 48 FF C6                                        inc     rsi
.text:00000000004008AC 32 14 0E                                        xor     dl, [rsi+rcx]
.text:00000000004008AF
.text:00000000004008AF                                                 L2_64:                                  ; CODE XREF: .text:00000000004008C1↓j
.text:00000000004008AF                                                                                         ; .text:00000000004008C6↓j
.text:00000000004008AF 8A 1C 08                                        mov     bl, [rax+rcx]
.text:00000000004008B2 30 D3                                           xor     bl, dl
.text:00000000004008B4 88 1C 08                                        mov     [rax+rcx], bl
.text:00000000004008B7 8A 74 08 FF                                     mov     dh, [rax+rcx-1]
.text:00000000004008BB 48 FF C1                                        inc     rcx
.text:00000000004008BE 80 FE FB                                        cmp     dh, 0FBh
.text:00000000004008C1 74 EC                                           jz      short L2_64
.text:00000000004008C3 80 FB 90                                        cmp     bl, 90h
.text:00000000004008C6 75 E7                                           jnz     short L2_64
.text:00000000004008C8 90                                              nop
.text:00000000004008C9 5B                                              pop     rbx
.text:00000000004008CA 9E                                              sahf
.text:00000000004008CA                                                 ; ---------------------------------------------------------------------------
.text:00000000004008CB 17                                              db  17h
.text:00000000004008CC 1B                                              db  1Bh
.text:00000000004008CD 5B                                              db  5Bh ; [
.text:00000000004008CE 3E                                              db  3Eh ; >
.text:00000000004008CF 93                                              db  93h
可以看出来代码已经解密出来
.text:000000000040087F 48 8D 04 08                                     lea     rax, [rax+rcx]
.text:0000000000400883 48 2D 80 00 00 00                               sub     rax, 80h
.text:0000000000400889 48 31 C9                                        xor     rcx, rcx
.text:000000000040088C
.text:000000000040088C                                                 LEn0_64:                                ; CODE XREF: .text:000000000040089B↓j
.text:000000000040088C 8A 1C 08                                        mov     bl, [rax+rcx]
.text:000000000040088F 30 D3                                           xor     bl, dl
.text:0000000000400891 88 1C 08                                        mov     [rax+rcx], bl
.text:0000000000400894 48 FF C1                                        inc     rcx
.text:0000000000400897 48 83 F9 20                                     cmp     rcx, 20h
.text:000000000040089B 7C EF                                           jl      short LEn0_64
.text:000000000040089D 48 05 80 00 00 00                               add     rax, 80h
.text:00000000004008A3 48 31 C9                                        xor     rcx, rcx
.text:00000000004008A6 8A 14 0E                                        mov     dl, [rsi+rcx]
.text:00000000004008A9 48 FF C6                                        inc     rsi
.text:00000000004008AC 32 14 0E                                        xor     dl, [rsi+rcx]
.text:00000000004008AF
.text:00000000004008AF                                                 L2_64:                                  ; CODE XREF: .text:00000000004008C1↓j
.text:00000000004008AF                                                                                         ; .text:00000000004008C6↓j
.text:00000000004008AF 8A 1C 08                                        mov     bl, [rax+rcx]
.text:00000000004008B2 30 D3                                           xor     bl, dl
.text:00000000004008B4 88 1C 08                                        mov     [rax+rcx], bl
.text:00000000004008B7 8A 74 08 FF                                     mov     dh, [rax+rcx-1]
.text:00000000004008BB 48 FF C1                                        inc     rcx
.text:00000000004008BE 80 FE FB                                        cmp     dh, 0FBh
.text:00000000004008C1 74 EC                                           jz      short L2_64
.text:00000000004008C3 80 FB 90                                        cmp     bl, 90h
.text:00000000004008C6 75 E7                                           jnz     short L2_64
.text:00000000004008C8 90                                              nop
.text:00000000004008C9 5B                                              pop     rbx
.text:00000000004008CA 9E                                              sahf
.text:00000000004008CA                                                 ; ---------------------------------------------------------------------------
.text:00000000004008CB 17                                              db  17h
.text:00000000004008CC 1B                                              db  1Bh
.text:00000000004008CD 5B                                              db  5Bh ; [
.text:00000000004008CE 3E                                              db  3Eh ; >
.text:00000000004008CF 93                                              db  93h
可以看出来代码已经解密出来
依次类推第一次输入的秘钥必须为:“”evXnaK“,从而让代码完全解密。本程序还有技巧是,当代码执行完后又将代码进行加密,可能这样的做法是为了增加当采用更改程序流程时,增加难度。先不管这个。
写了脚本将程序对程序进行去除花指令和解密后main函数代码大概如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi
  int result; // eax
  const char **v5; // [rsp+20h] [rbp-40h]
  int v6; // [rsp+2Ch] [rbp-34h]
  __int64 v7; // [rsp+30h] [rbp-30h]
  __int64 v8; // [rsp+38h] [rbp-28h]
  __int64 v9; // [rsp+40h] [rbp-20h]
  __int64 v10; // [rsp+48h] [rbp-18h]
  __int64 v11; // [rsp+50h] [rbp-10h]
  unsigned __int64 v12; // [rsp+58h] [rbp-8h]

  v6 = argc;
  v5 = argv;
  v12 = __readfsqword(0x28u);
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  welcome(v3, 0LL);
  syscall; LINUX - sys_mprotect
  __asm { syscall; LINUX - sys_read }  
  __asm { syscall; LINUX - sys_write }
  fflush(0LL);
  lpGoble = (char *)&v5;
  __asm { syscall; LINUX - sys_read }
  printf((const char *)&v5, &v5, 26LL, argv);
  __asm
  {
    syscall; LINUX - sys_read
    syscall; LINUX - sys_mprotect
  }
  result = 0;
  JUMPOUT(__readfsqword(0x28u), v12, &locret_400ABA);
  return result;
}

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi
  int result; // eax
  const char **v5; // [rsp+20h] [rbp-40h]
  int v6; // [rsp+2Ch] [rbp-34h]
  __int64 v7; // [rsp+30h] [rbp-30h]
  __int64 v8; // [rsp+38h] [rbp-28h]
  __int64 v9; // [rsp+40h] [rbp-20h]
  __int64 v10; // [rsp+48h] [rbp-18h]
  __int64 v11; // [rsp+50h] [rbp-10h]
  unsigned __int64 v12; // [rsp+58h] [rbp-8h]

  v6 = argc;
  v5 = argv;
  v12 = __readfsqword(0x28u);
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  welcome(v3, 0LL);
  syscall; LINUX - sys_mprotect
  __asm { syscall; LINUX - sys_read }  
  __asm { syscall; LINUX - sys_write }
  fflush(0LL);
  lpGoble = (char *)&v5;
  __asm { syscall; LINUX - sys_read }
  printf((const char *)&v5, &v5, 26LL, argv);
  __asm
  {
    syscall; LINUX - sys_read
    syscall; LINUX - sys_mprotect
  }
  result = 0;
  JUMPOUT(__readfsqword(0x28u), v12, &locret_400ABA);
  return result;
}

四、分析真正的main函数
1、当输入 evXnaK 之后,程序将在终端输出"wow!\n"
2、再次从终端读取0x1A大小的数据到局部变量中(堆栈中)。
3、然后将读到的数据写入到终端中。
4、再次读取0x200个大小的数据到局部变量中 (堆栈中)。
5、将调用mprotect将程序改成只读只执行,不可写。
6、结束程序。
五、分析漏洞
从上面来看,存在3个漏洞:
1、将用户输入的数据存储在局部变量中,并且调用printf打印出来,利用printf格式化字符串漏洞可以实现任意地址读写(百度告诉的,之前不知道哈)
2、读取0x200个数据到堆栈中,如果用户输入0x200个数据,明显会造成堆栈溢出。可以使用缓冲区溢出漏洞进行攻击。(这个还是了解的)
3、 本题为了实现SMC,必须调用mprotect对代码进行解密,程序最后也将代码改成不可写了,但是调用mprotect的时机晚了,其在main函数结束后才调用。
       但是调用printf时可以对任意地址读写的。如果将代码解密后再立即调用mprotect这个题难度可能会更高。

4、调用checksec wow看下程序的保护机制 如下:
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
可以看 .plt.got段可读写 ,同时没有开启pie,表示程序代码段地址不变。但是开启了堆栈检测,并且堆栈不可执行。
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
可以看 .plt.got段可读写 ,同时没有开启pie,表示程序代码段地址不变。但是开启了堆栈检测,并且堆栈不可执行。

6、如何利用漏洞?
       程序在在调用printf时,代码段是可以写的,因此我们可以使用printf漏洞更改程序的执行流程,让代码一致进行如下循环:
while(1)
{
	read(0, buf1,0x1a)
	printf(buf1)
	read(0,buf2,0x200)
}

while(1)
{
	read(0, buf1,0x1a)
	printf(buf1)
	read(0,buf2,0x200)
}

起始可以将最后的read(0, buf2,0x200)也去掉。
相关对应的汇编代码如下:
.text:0000000000400A30 48 31 C0                                        xor     rax, rax
.text:0000000000400A33
.text:0000000000400A33                                                 loc_400A33:                             ; CODE XREF: .text:L_J0↑j
.text:0000000000400A33 48 C7 C2 1A 00 00 00                            mov     rdx, 1Ah
.text:0000000000400A3A 48 89 E6                                        mov     rsi, rsp
.text:0000000000400A3D 48 89 24 25 88 10 60 00                         mov     ds:qword_601088, rsp
.text:0000000000400A45 48 89 C7                                        mov     rdi, rax
.text:0000000000400A48 0F 05                                           syscall                                 ; LINUX - sys_read
.text:0000000000400A4A 48 8B 05 37 06 20 00                            mov     rax, cs:qword_601088
.text:0000000000400A51 48 89 C7                                        mov     rdi, rax
.text:0000000000400A54 B8 00 00 00 00                                  mov     eax, 0
.text:0000000000400A59 E8 52 FB FF FF                                  call    sub_4005B0
.text:0000000000400A5E 48 31 C0                                        xor     rax, rax
.text:0000000000400A61 48 C7 C2 00 02 00 00                            mov     rdx, 200h
.text:0000000000400A68 48 8D 74 24 E0                                  lea     rsi, [rsp-20h]
.text:0000000000400A6D 48 89 C7                                        mov     rdi, rax
.text:0000000000400A70 0F 05                                           syscall                                 ; LINUX - sys_read
.text:0000000000400A72 90                                              nop
.text:0000000000400A73 E8 00 00 00 00                                  call    $+5

.text:0000000000400A30 48 31 C0                                        xor     rax, rax
.text:0000000000400A33
.text:0000000000400A33                                                 loc_400A33:                             ; CODE XREF: .text:L_J0↑j
.text:0000000000400A33 48 C7 C2 1A 00 00 00                            mov     rdx, 1Ah
.text:0000000000400A3A 48 89 E6                                        mov     rsi, rsp
.text:0000000000400A3D 48 89 24 25 88 10 60 00                         mov     ds:qword_601088, rsp
.text:0000000000400A45 48 89 C7                                        mov     rdi, rax
.text:0000000000400A48 0F 05                                           syscall                                 ; LINUX - sys_read
.text:0000000000400A4A 48 8B 05 37 06 20 00                            mov     rax, cs:qword_601088
.text:0000000000400A51 48 89 C7                                        mov     rdi, rax
.text:0000000000400A54 B8 00 00 00 00                                  mov     eax, 0
.text:0000000000400A59 E8 52 FB FF FF                                  call    sub_4005B0
.text:0000000000400A5E 48 31 C0                                        xor     rax, rax
.text:0000000000400A61 48 C7 C2 00 02 00 00                            mov     rdx, 200h
.text:0000000000400A68 48 8D 74 24 E0                                  lea     rsi, [rsp-20h]
.text:0000000000400A6D 48 89 C7                                        mov     rdi, rax
.text:0000000000400A70 0F 05                                           syscall                                 ; LINUX - sys_read
.text:0000000000400A72 90                                              nop
.text:0000000000400A73 E8 00 00 00 00                                  call    $+5


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-6-22 11:49 被oooAooo编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 8224
活跃值: (1296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享,
学习了
2018-6-28 18:20
0
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
3
不知楼主是否可以吧SMC那段的解密脚本发一下,以供学习,谢谢,盼复
2018-9-10 21:01
0
雪    币: 13059
活跃值: (5723)
能力值: ( LV5,RANK:77 )
在线值:
发帖
回帖
粉丝
qux
4
不太理解的是system的参数,64位程序第一个参数不应该是放在rdi寄存器中吗,为什么把'/bin/sh'放在rsi中也可以呢?
2018-10-29 21:39
0
游客
登录 | 注册 方可回帖
返回
//