Sunday, November 28, 2010

PHP file upload (คำถาม)

ระหว่างคอยเนื้อหาการเขียน exploit ที่ผมต้องใช้เวลาอีกซักพัก ในการเตรียมและทดสอบหลายๆ อย่าง
คราวนี้ผมให้เป็นโจทย์ไปดีกว่า (ผมจะได้ไม่ต้องใช้เวลาเขียนเยอะ :D) แล้วผมคอยมาเฉลย(อธิบาย) ทีหลัง

เรื่องนี้ก็เป็นไปตามชื่อเรื่อง ตอนแรกผมก็คิดเล่นๆ แต่พอลอง search ดู พบว่า code ตัวอย่างใน w3cshools ก็มีช่องโหว่ เลยเอามาให้ทำซะเลย (ให้ setup web server เอาเองนะครับ)

ปัญหาที่ 1 (upload1.php) (copy มาจาก http://w3schools.com/PHP/php_file_upload.asp)

<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000))
  {
  if ($_FILES["file"]["error"] > 0)
    {
    echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
    }
  else
    {
    echo "Upload: " . $_FILES["file"]["name"] . "<br />";
    echo "Type: " . $_FILES["file"]["type"] . "<br />";
    echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
    echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";

    if (file_exists("upload/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload/" . $_FILES["file"]["name"]);
      echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
      }
    }
  }
else
  {
  echo "Invalid file";
  }

code ข้างบนมีช่องโหว่อยู่ หาวิธี upload php file ให้ได้ (ใจดีนะเนี่ย ที่บอกว่าให้ทำอะไร จริงๆแล้ว ต้องรู้เองว่าต้องทำอะไร)

โจทย์ข้างบนผมว่าง่ายเกินไป ผมเลยขอแก้ไข code ข้างบน (ให้อ่านง่ายขึ้นด้วย) ยากขึ้นอีกนิดหน่อย

ปัญหาที่ 2 (upload2.php)

<?php
if ($_FILES["file"]["type"] == "image/png") {
  if ($_FILES["file"]["error"] > 0) {
    echo "Return Code: " . $_FILES["file"]["error"];
  }
  else {
    echo "Upload: " . $_FILES["file"]["name"] . "<br />";
    echo "Type: " . $_FILES["file"]["type"] . "<br />";
    echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
    echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";

    $info = getimagesize($_FILES["file"]["tmp_name"]);
    if ($info == FALSE) {
      echo "Invalid image file";
    }
    else if ($info["mime"] != "image/png") {
      echo "Image is not PNG";
    }
    else if (file_exists("upload/" . $_FILES["file"]["name"])) {
      echo $_FILES["file"]["name"] . " already exists. ";
    }
    else {
      move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
      echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
    }
  }
}
else {
  echo "Invalid file";
}

ปัญหาที่ 2 เพิ่มการตรวจสอบอีกนิดหน่อย ผมว่าก็ยังง่ายอยู่นะ

คนรู้อยู่แล้ว หรือทำได้แล้ว ก็ไม่ต้องเขียนเฉลยนะครับ ให้คนอื่นเขาหาทางกันเองก่อน จริงๆ แล้วเดี๋ยวผมไม่มีอะไรให้เขียน :D

Update: ผมได้ publish เฉลยแล้วนะครับ ถ้าไม่อยากคิดก็ กดเลยครับ

Saturday, November 27, 2010

Buffer Overflow คืออะไร

ก่อนเริ่ม ผมขอบอกก่อนว่า ในที่นี้จะกล่าวถึงเฉพาะ Linux ใครที่สนใจ Windows คงต้องคอยอีกนาน (จนกว่าผมจะหมดวิธีเขียน exploit ต่างๆ บน Linux) โดยตัวอย่าง(น่าจะ)ทั้งหมด ผมจะใช้ Ubuntu 10.04 (32 bit) ดังนั้นถ้าใครสนใจ ก็เตรียม VMWare หรือ VirtualBox หรือ ...(แล้วแต่ถนัด) ไว้ได้เลย
ถึงแม้ว่าคุณจะใช้ Ubuntu 10.04 เป็น OS หลักอยู่แล้ว ผมก็แนะนำให้ลงในพวก Virtual Machine เพราะจะต้องมีการใส่ code ที่มีช่องโหว่ (Vulnerable code) และปิด security feature บางอย่างด้วย

เรามาเริ่มกันเลยดีกว่า...

ในทางโปรแกรมมิ่ง buffer หมา่ยถึง memory ที่จองไว้ สำหรับทำงานต่างๆ เช่น รับข้อมูลจากผู้ใช้ ใส่ข้อมูลเก็บชั่วคราว
็Buffer Overflow ก็คือ การใส่ข้อมูลใน buffer เกินที่จองไว้ เช่นเราได้กำหนดขนาด buffer ไว้ 8 bytes แต่เราใส่ข้อมูลไป 12 bytes

Buffer Overflow เป็นปัญหาหนึ่งในเรื่อง security โดยในเริ่มต้น จะยกตัวอย่างที่ง่ายๆ

ตัวอย่างที่ 1 (ex_01_1.c)

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

int main(int argc, char **argv)
{
  int magic = 0;
  char buf[8];

  printf("Before strcpy: magic is 0x%08x\n", magic);
  strcpy(buf, argv[1]);
  printf("After strcpy: magic is 0x%08x\n", magic);
  if (magic == 0x55555555)
    printf("Hahaha, you WIN\n");

  return 0;
}

build code ตัวอย่างที่ 1 ด้วยคำสั่งข้างล่างไม่ต้องพิมพ์ $ นะครับ (option ของ gcc จะมีการกล่าวถึงในหัวข้อถัดๆ ไป)

$ gcc -mpreferred-stack-boundary=2 -fno-stack-protector -o ex_01_1 ex_01_1.c

ใครที่มีความรู้เรื่องนี้มาบ้าง ก็คงจะรู้แล้วว่าผมจะให้ทำอะไร จากตัวอย่างนี้ ให้รันโปรแกรม แล้วให้โปรแกรมพิมพ์คำว่า "Hahaha, you WIN" ให้ได้

มาดูเฉลยกันเลยดีกว่า

จะเห็นว่ามีการจอง buffer ไว้ 8 bytes ครั้งแรกขอลองใส่ 8 ตัว

$ ./ex_01_1 55555555
Before strcpy: magic is 0x00000000
After strcpy: magic is 0x00000000
คราวนี้ลองใส่ 9 ตัว
$ ./ex_01_1 555555555
Before strcpy: magic is 0x00000000
After strcpy: magic is 0x00000035
จะเห็นว่ามีเลข 35 ออกมาตัวหนึ่ง คราวนี้ลองใส่ 12 ตัว
$ ./ex_01_1 555555555555
Before strcpy: magic is 0x00000000
After strcpy: magic is 0x35353535
จะเห็นว่าคราวนี้ magic เป็น 35 หมดเลย ถ้าเราเป็น ASCII table ดู จะเห็นว่าเลข 35 ในฐาน 16 คือตัวอักษร "5" (หลังจากนี้ ผมจะเขียนเลขฐาน 16 เป็น 0x35 หรือ 35h) แต่ที่เราต้องการคือ 55h เมื่อดูที่ ASCII table 55h คือตัวอักษร "U" คราวนี้เราลองใส่เป็น U 12 ตัว
$ ./ex_01_1 UUUUUUUUUUUU
Before strcpy: magic is 0x00000000
After strcpy: magic is 0x55555555
Hahaha, you WIN
:O ทำได้แล้ว จะเห็นว่าเราทำการ copy ข้อมูลที่รับมาจาก argv[1] ลงที่ buf แต่ค่า magic กับถูกเปลี่ยนไปด้วย ที่เป็นแบบนี้ เพราะว่า magic ได้ถูกจองไว้หลัง buf ซึ่งเมื่อมีการเขียนข้อมูลลง buf เกินไว้ที่จอง ข้อมูลที่เกินก็จะถูกเขียนไปที่ magic
คราวนี้ เรามาลองเพิ่มเติม โดยการใส่ตัว U ไป 16 ตัว
$ ./ex_01_1 UUUUUUUUUUUUUUUU
Before strcpy: magic is 0x00000000
After strcpy: magic is 0x55555555
Hahaha, you WIN
Segmentation fault
o.O Segmentation fault คืออะไร ค่านี้เราไม่มีการสั่งให้โปรแกรมแสดงออกมา
Segmentation fault เกิดเมื่อโปรแกรมมีการ access memory ที่ OS ไม่อนุญาต และที่เกิดขึ้นเพราะว่า เราได้เขียนทับข้อมูลส่วนที่ใช้สำหรับควบคุมการทำงานของโปรแกรม ซึ่งทำให้โปรแกรมทำงานผิดพลาด ซึ่งในกรณีนี้เราจะกลับมาดูอีกครั้ง ว่าเราสามารถทำอะไรได้บ้าง >=)

น่าสนใจไหมครับ... แต่ก่อนที่จะถึงส่วนที่สนุก ผมต้องอธิบายเกี่ยวกับความรู้พื้นฐานสำหรับการเขียน exploit ก่อน เช่น memory layout, assembly, ELF, ... ซึ่งเรื่องพวกนี้ ผมคิดว่าน่าเบื่อ แต่จำเป็นต้องรู้

สุดท้าย tutorial การเขียน exploit ชุดนี้ สำหรับคนที่รู้ภาษา C และ Linux command พอสมควร ถ้าใครอ่านตอนนี้ไม่รู้เรื่อง แล้วสนใจ คงต้องไปหาความรู้เพิ่มเติมนะครับ

CBC Bit Flipping

encryption algorithm โดยทั่วไป จะทำการเข้ารหัสเป็น block เช่น AES จะมี block size เป็น 128 bits (16 bytes) ซึ่งถ้าเรานำ key มาเข้ารหัสข้อมูลทีละ block ก็จะเรียก mode นี้ว่า ECB (Electronic CodeBook) ตามรูปข้างล่าง (รูปจาก http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation)


รูปข้างบนสำหรับ encryption และรูปข้างล่างสำหรับ decryption ปัญหาของ mode นี้ไม่ขอกล่าวในนี้ เพราะว่าน่าจะมีอยู่แล้วในหนังสือ cryptography ทั่วไป

สิ่งที่ผมต้องการจะพูดถึงคือ CBC (Cipher Block Chaining) mode ซึ่งมีการเข้ารหัสตามภาพข้างล่าง (รูปจาก) http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation)


IV (Initialization Vector) คือค่าเริ่มต้น โดยปกติจะได้จากการ random ขนาดเท่ากับ block operation ของ algorithm นั้นๆและเพื่อให้ง่ายต่อการอธิบายต่อ ผมจะขอเรียกผลลัพธ์จากการ decryption ที่ยังไม่ทำ XOR ว่า "Intermediate value"

ใน mode นี้ ก่อนที่จะนำข้อมูลไปเข้ารหัส จะไปทำ XOR กับผลลัพธ์จากการเข้ารหัสของ block ก่อนหน้า ส่วน block แรกจะทำการ XOR กับ IV ส่วนการทำ decryption ก็จะทำในทางกลับกัน ตามรูปข้างบน

เมื่อเข้าใจ CBC mode กันแล้ว คราวนี้ก็มาถึง attack หนึ่งเพื่อทำ bit flipping ซึ่ง attack ที่ขั้นตอนการทำ XOR โดย XOR มีคุณสมบัติดังนี้ (สำหรับคนที่ยังไม่รู้)
A xor 0 = A
A xor 1 = not A
ถ้า A xor B = C แล้ว B xor C = A, C xor A = B
จากรูปการ decryption ของ CBC mode ถ้าเราสนใจใน block ที่ 3 จะเห็นว่า intermediate value ของ block ที่ 3 จะทำการ XOR กับ encrypted block ที่ 2 ได้ผลลัพธ์ออกมา ดังนั้นถ้าเราแก้ค่าใน encrypted block ที่ 2 ไป 1 bit ผลของการ decryption ใน block ที่ 3 ก็จะเปลี่ยนไป 1 bit แต่ผลของการ decryption ใน block ที่ 2 จะเปลี่ยนแปลงทั้งหมด ตามรูปข้างล่าง


attack นี้จะทำได้ เราต้องรู้ว่า plaintext บางส่วน(หรือทั้งหมด) แล้วเราต้องการเปลี่ยนค่าบ้างค่า และที่สำคัญข้อมูลใน block ที่ถูกเปลี่ยนแปลงทั้งหมดนั้น เป็นเพียงข้อมูล ที่ไม่ผลต่อการทำงานของโปรแกรมที่อ่านข้อมูล

ถ้าเรารู้ข้อมูล plaintext และตำแหน่งทั้งหมด เราสามารถที่จะเปลี่ยนข้อมูลได้ทั้ง block เนื่องจากเราสามารถหา intermediate value ของ block ที่เราจะเปลี่ยนได้จากการ XOR กันของ plaintext กับ encrypted block ก่อนหน้า และนำ plaintext ที่เราต้องการจะให้เป็นไป XOR กับ intermediate value ก็จะได้ encrypted block ก่อนหน้าที่เราต้องเปลี่ยน

ถึงแม้ว่า attack นี้จะมีมานานแล้ว แต่ถือว่าเป็นตัวอย่างที่ดีว่า การทำ encryption ช่วยได้เฉพาะเรื่องความลับของข้อมูล (Confidential) แต่ไม่ได้ป้องกันเรื่อง ความถูกต้องของข้อมูล (Integrity)

Wednesday, November 17, 2010

ทดสอบ Hello

ทดสอบ blog ใหม่ สำหรับ tutorial ภาษาไทย เนื้อหาจะเกี่ยวกับการ hack ก็น่าจะมีเกี่ยวกับ
- การเขียน exploit
- cryptograhy (การเข้ารหัส ถอดรหัส)
- web attack (sql injection, xss, ...)
- ... คิดไม่ออก

ยังไงก็ต้องใช้ Syntax Highlighter ในการเขียน ก็เลยขอลองซะเลย

#include <stdio.h>
int main() {
    printf("Hello World :)");
    return 0;
}

อันนี้ใช้ของ http://www.balupton.com/sandbox/jquery-syntaxhighlighter/demo/