Tag: linux

  • เทคนิคการเขียน Shell Script #1

    เมื่อต้องการเขียน Shell Script เพื่อรับ Argument และ Option เช่น เขียน myscript.sh ซึ่งแบ่งเป็น 3 แบบ
    1) $ sh myscript1.sh myfirstname mylastname 16
    2) $ sh myscript2.sh -f myfirstname -l mylastname -a 16
    3) $ sh myscript3.sh –firstname myfirstname –lastname mylastname –age 16

    [บทความนี้ ใช้งานบน Ubuntu 12.04 Server และ ใช้ Bash Shell]

    1) วิธีแรก คือ การรับตัว Argument เรียงตามลำดับ โดยใช้ เครื่องหมาย $ ตามด้วยลำดับของตัวแปร เช่น $1, $2, $3 ดัง Script myscript1.sh มีรายละเอียด ต่อไปนี้

    firstname=$1
    lastname=$2
    age=$3
    echo "Firstname=$firstname"
    echo "lastname=$lastname"
    echo "age=$age"

    วิธีนี้ ข้อดีคือ สร้างง่าย แต่ ข้อเสียคือ ต้องใส่ Argument ตามลำดับเท่านั้น

    2) วิธีที่สอง คือ มีการใช้ Option แบบชื่อสั้น เช่น -f, -l ,-a โดยต้องใช้คำสั่ง getopts ดังตัวอย่าง Script myscript2.sh

    getopts "f:l:a:" opt
    while [ "$opt" != "?" ]
    do
       case $opt in
        f)
         echo "Firstname: $OPTARG"
         ;;
        l)
         echo "Lastname: $OPTARG"
         ;;
        a)
         echo "Age: $OPTARG"
         ;;
        esac
     getopts "f:l:a:" opt
    done

    อธิบายเพิ่มเติม

    • getopts "f:l:a:" opt
      คำสั่งนี้ บอกว่า getopts จะรับ Option คือ f, l และ a และ เมื่อรับมาแล้ว จะใส่ในตัวแปร opt
      ส่วนใน “f:l:a:” นั้น เป็นการกำหนด Short Option Name โดยที่ เป็นอักษรตัวเดียว และ ค่า Option ใด ไม่มี เครื่องหมาย “:” แสดงว่า ไม่ต้องการใส่ค่า
      Option ใด มี     เครื่องหมาย “:” แสดงว่า ต้องการมีการใส่ค่า
      Option ใด ไม่มี เครื่องหมาย “::” แสดงว่า จะใส่ค่า หรือไม่ใส่ค่า ก็ได้
    • while [ "$opt" != "?" ] … done
      วนลูป ทุก Options จนเจอค่า “?” จึงหยุดทำงาน
    • case $opt in … esac
      เป็นการ Switch Case ตัว Option ที่เข้ามา ระหว่าง f, l และ a ให้ทำงานตามสั่ง
    • $OPTARG
      เป็นค่าที่ให้มา ผ่าน Option นั้นๆ เช่น หากมีการสั่งงานด้วยคำสั่ง

      sh myscript2.sh -f Somchai -l Jaidee -a 16
      เมื่อลูป ค่า $opt เป็น l , ก็จะได้ค่า $OPTARG เป็น Somchai เป็นต้น

    วิธีนี้ มีข้อดีคือ ง่ายต่อการพัฒนา สามารถ สลับตำแหน่งของ Option ได้ดีกว่าวิธีแรก แต่มีข้อเสียคือ ชื่อย่อ ของ Option นั้นสั้น อาจจะทำให้ยากต่อการสื่อสาร

    3) วิธีที่สาม คือ มีการใช้งาน Option แบบชื่อยาว เช่น –firstname, –lastname, –age
    ตัวอย่าง

    if ! options=$(getopt -o f:l:a: -l firstname:,lastname:,age: -- "$@")
    then
     exit 1
    fi
    
    set -- $options
    
    while [ $# -gt 0 ]
    do
     case $1 in
       -f|--firstname) echo "Firstname: $2" ; shift 2;;
       -l|--lastname) echo "Lastname : $2"; shift 2 ;;
       -a|--age) echo "Age: $2" ; shift 2 ;;
       --) shift ; break;;
     esac
    done
    

    ทั้งนี้ สิ่งที่แตกต่างระหว่างวิธีที่ 2) และ 3) คือ การใช้งาน “getopts” นั้น จะไม่สามารถใส่ Long Options ได้ ต้องใช้ “getopt” จึงจะใช้งานได้ นอกนั้น ก็คล้ายๆกันครับ

  • วิธีตรวจสอบเว็บไซต์ที่โดน Hack #12

    บทความนี้ จะกล่าวถึง วิธีการปิดช่องโหว่ของ Apache ที่ให้บริการ Web Hosting เลย เผื่อมีผู้ใช้ ติดตั้ง Joomla และมี JCE รุ่นที่มีช่องโหว่ จะได้ไม่สร้างปัญหา และ แนะนำวิธีสำหรับผู้พัฒนาเวปไซต์เองด้วย ที่เปิดให้มีการ Upload ไฟล์โดยผู้ใช้ผ่านทาง Web ด้วย … เพราะหน้าที่นี้ ควรเป็นของ Web Server Administrator ไม่ใช่ของ Web Master หรือ Web Author ครับ

    จากปัญหาช่องโหว่ของ JCE Exploited ของ Joomla ที่อธิบายไว้ใน วิธีตรวจสอบเว็บไซต์ที่โดน Hack #3 ที่อธิบายขั้นตอนการเจาะช่องโหว่, วิธีตรวจสอบเว็บไซต์ที่โดน Hack #4 ซึ่งเป็นวิธีการตรวจสอบค้นหา และทำลาย Backdoor และ วิธีตรวจสอบเว็บไซต์ที่โดน Hack #11  วิธีการ Incremental Backup ซึ่งสามารถช่วยกู้ไฟล์ได้และรู้ว่า มีไฟล์แปลกปลอมอะไรเกิดบ้าง ซึ่งเป็นการแก้ปัญหาปลายเหตุทั้งสิ้น

    ส่วน วิธีตรวจสอบเว็บไซต์ที่โดน Hack #5 กล่าวถึงการตรวจสอบว่า Software ที่ใช้งานอยู่มีช่องโหว่อะไรบ้าง ด้วยการตรวจสอบ CVE เป็นต้น

    สำหรับ JCE Exploited จะพบว่า การวางไฟล์ Backdoor จะเริ่มวางไว้ที่ไดเรคทอรี่ images/stories ที่ แกล้งเป็นไฟล์ .gif แล้วเอาโค๊ด PHP เข้ามาใส่ แล้วเปลี่ยนนามสกุลเป็น .php ภายหลัง ดังนั้น หาก Apache Web Server สามารถ ปิดกั้นตั้งแต่จุดนี้ได้ กล่าวคือ ต่อให้เอาไฟล์มาวางได้ แต่สั่งให้ทำงานไม่ได้ ก็น่าจะปลอดภัย และ หากพัฒนาเวปไซต์เอง หรือ ผู้ใช้ของระบบต้องการให้ Upload ไฟล์ไว้ในไดเรคทอรี่ใดได้ ก็ต้องแจ้งให้ Web Server Administrator รับทราบ และเพิ่มเติมให้ น่าจะทำให้ปลอดภัยมากขึ้นได้

    สมมุติฐานคือ

    1. ติดตั้ง OS และ Apache Web Server ใหม่

    2. ติดตั้ง Joomla ใหม่ หรือ เอา Web Application ที่ปลอดช่องโหว่อื่นๆ/Backdoor มาลง
      โดย Joomla ที่มีที่วางไฟล์ภาพไว้ที่ images/stories ส่วน Web Application อื่นๆ ขอสมมุติว่าตั้งชื่อไดเรคทอรี่ว่า uploads

    สำหรับ Apache2 บน Ubuntu Apache 2.2 นั้น มีโครงสร้างไดเรคทอรี่ดังนี้

    /etc/apache2/
    |-- apache2.conf
    |       `--  ports.conf
    |-- mods-enabled
    |       |-- *.load
    |       `-- *.conf
    |-- conf.d
    |       `-- *
    |-- sites-enabled
            `-- *

    เมื่อ Apache เริ่ม (Start) ก็จะไปอ่าน /etc/apache2/apache2.conf ในนั้น จะกำหนดภาพรวมของระบบ ได้แก่ ใครเป็นคน Start (APACHE_RUN_USER/APACHE_RUN_GROUP), การกำหนดชื่อไฟล์ .htaccess ที่เปิดให้ผู้ใช้ปรับแต่ง Apache ที่แต่ละไดเรคทอรี่ของตนได้, กำหนดว่า จะเรียกใช้ Module อะไรบ้าง โดยการ Include *.load, *.conf  จาก mods-enabled, กำหนดว่า จะเปิดให้มี Virtual Host อะไรบ้างโดยการ Include ไฟล์จาก sites-enabled และ ที่สำคัญ ผู้ดูแลระบบสามารถแยกไฟล์ config ออกเป็นส่วนย่อยๆเป็นไฟล์ โดยการ Include จาก conf.d

    ต่อไป สร้างไฟล์ /etc/apache2/conf.d/jce มีเนื้อหาดังนี้

    <DirectoryMatch ".*/images/stories/.*">
    <FilesMatch "\.php$">
           Order Deny,Allow
           Deny from All
     </FilesMatch>
    </DirectoryMatch>

     และในทำนองเดียวกัน สร้างไฟล์ /etc/apache2/conf.d/uploads มีเนื้อหาดังนี้

    <DirectoryMatch ".*/uploads/.*">
    <FilesMatch "\.php$">
           Order Deny,Allow
           Deny from All
     </FilesMatch>
    </DirectoryMatch>

    ก่อนจะ Restart/Reload Apache ทดสอบสร้างไฟล์ใน

    /var/www/joomla15/images/stories/0day.php
    /var/www/mywebapp/uploads/hack.php

    เมื่อเรียก URL
    http://localhost/joomla15/images/stories/0day.php

    http://localhost/mywebapp/uploads/hack.php

     ผลที่ได้คือ Backdoor หน้าตาประมาณนี้

    แต่พอใช้ Reload Apache ด้วยคำสั่ง

     sudo /etc/init.d/apache2 reload

     แล้วเรียก URL

     http://localhost/joomla15/images/stories/0day.php

     จะได้ผลดังนี้

    เป็นอันว่า แม้ Hacker จะสามารถเอาไฟล์ 0day.php ไปวางใน images/stories ได้ แต่ก็จะไม่สามารถทำงานได้ (อย่างน้อย ก็เรียกใช้ไม่ได้ แต่ผู้ดูแลต้องค้นหาและทำลายเป็นประจำ)

     อธิบายเพิ่มเติมเกี่ยวกับ Apache Configuration เล็กน้อย, การเขียนนั้น ประกอบด้วยสิ่งที่เรียกว่า Directive โดยแบ่งออกเป็น Container และ Directive ทั่วไป

    1. Container Directive: เป็นตัวบอกขอบเขต แบ่งออกเป็น

    1.1 FileSystem: ได้แก่

    1.1.1 <Directory directory-path> … </Directory>
    ตั้งค่ากับเฉพาะ ขอบเขตของ Directory ซึ่ง directory-path จะต้องเขียนตามให้เต็ม Path เช่น
    <Direcotory /var/www>
    ….
    </Directory>

    1.1.2 <DirectoryMatch regexp> … </DirectoryMatch>
    ตั้งค่ากับเฉพาะ ขอบเขตของ Directory ซึ่งสอดคล้องกับ regexp ที่กำหนด เช่น
    <DirecotoryMatch “.*/images/stories/.*”>
    ….
    </DirectoryMatch>

    1.1.3 <Files filename> … </Files>
    ตั้งค่ากับเฉพาะ ชื่อไฟล์ที่ตรงกับ filename ที่กำหนด เช่่น
    <Files “somefile.html”>

    </Files>

    1.1.4 <FilesMatch regexp> … </FilesMatch>
    ตั้งค่ากับเฉพาะ ชื่อไฟล์ที่สอดคล้องกับ regexp ที่กำหนด เช่่น
    <FilesMatch “.*\.php$”>

    </FilesMatch>

    1.2 WebSpace: ได้แก่

    1.2.1 <Location URL-Path> … </Location>
    ตั้งค่ากับเฉพาะ URL ที่ตรงกับ URL-Path เช่น
    <Location /private>

    </Location>
    1.2.2 <LocationMatch regexp> … </LocalMatch>
    ตั้งค่ากับเฉพาะ URL ที่สอดคล้องกับ regexp เช่น
    <LocationMatch “/(extra|special)/data”>

    </LocationMatch>

    2. Other Directive
    ซึ่งมีอยู่มากมาย กรุณาอ่านเพิ่มเติมจาก http://httpd.apache.org/docs/2.2/mod/core.html แต่ในที่นี้ จะขอยกตัวอย่างที่สำคัญ และจำเป็นต้องใช้ ตามตัวอย่างข้างต้น คือ

    Order ordering : อยู่ใน Module mod_access_compat, ค่า ordering ที่สามารถกำหนดได้คือ

    Allow, Deny ซึ่งจะพิจารณาการอนุญาตก่อนปฏิเสธ และ Deny, Allow จะปฏิเสะก่อนแล้วพิจารณาอนุญาต ให้เข้าถึงไฟล์ หรือ ไดเรคทอรี่ต่างๆ

    Deny all|host : อยู่ใน Module mod_access_compat, ค่า all หมายถึง ปฏิเสธทุกการเชื่อมต่อจากทุกๆที่, host สามารถเป็น IP Address หรือ URL ก็ได้

    Allow all|host : อยู่ใน Module mod_access_compat, ค่า all หมายถึง ยอมรับทุกการเชื่อมต่อจากทุกๆที่, host สามารถเป็น IP Address หรือ URL ก็ได้

    ดังนั้น ไฟล์ /etc/apache2/conf.d/jce ซึ่งมีเนื้อหาว่า

    <DirectoryMatch ".*/images/stories/.*>
     <FilesMatch "\.php$">
           Order Deny,Allow
           Deny from All
     </FilesMatch>
    </DirectoryMatch>

    หมายถึง ถ้ามีการเรียก ไฟล์ที่อยู่ใน directory อะไรก็ตามที่มีส่วนหนึ่งของ Path เป็น images/stories ก็จะ ไปดูว่า ชื่อไฟล์ที่เรียกนั้น มีนามสกุลเป็น .php หรือไม่ (.* แปลว่า ตัวอักษรอะไรก็ได้, \. หมายถึงจุด “.” ที่ใช้เชื่อม filename และ extenstion และ $ หมายถึง สิ้นสุดข้อความ) ถ้าเป็นการเรียกไฟล์ .php ใน images/stories ก็จะ ปฏิเสธเสมอ (Deny from ALL)

    แล้ว ทำไมไม่ใช่ .htaccess ?

    จาก Apache Security Tips ไม่แนะนำให้ใช้ .htaccess เพราะปัญหาด้าน Performance เพราะทุกครั้งที่จะเข้าถึงไฟล์ จะต้องพิจารณา .htaccess ทุกครั้ง ในเวปไซต์ที่มีการใช้งานมาก อาจจะทำให้ความเร็วช้าลงได้ อีกประการหนึ่ง .htaccess นั้นอยู่ในไดเรคทอรี่ที่ผู้ใช้สามารถกำหนดสิทธิ์ (Permission) เองได้ หากพลาดกำหนดให้ Web User สามารถเขียนได้ อาจจะทำให้ Hacker เลี่ยงข้อกำหนดต่างๆได้ หาก ที่ Apache Main Configuration ประกาศ AllowOverride เป็น ALL

    ขอให้โชคดี

    Reference

    [1] “Apache HTTP Server Version 2.2 Documentation – Apache HTTP …” 2005. 7 Jan. 2014 <http://httpd.apache.org/docs/2.2/> .

    [2] “Security Tips – Apache HTTP Server.” 2005. 7 Jan. 2014 <http://httpd.apache.org/docs/2.2/misc/security_tips.html>

  • วิธีตรวจสอบเว็บไซต์ที่โดน Hack #8

    ได้รับข้อร้องเรียนจาก Google Webmaster Tools ว่า มีเครื่องภายในมหาวิทยาลัย พยายามโจมตี เครือข่ายภายนอก และทาง Firewall ของมหาวิทยาลัย ได้ทำการปิดกั้น การเข้าออก ของเครื่องดังกล่าวแล้ว จึงเข้าตรวจสอบ

     ขั้นตอนการตรวจสอบ

     1. เบื้องต้น พบว่าเป็น  Ubuntu 8.04.4 LTS

    2. ตรวจสอบ ทำให้ทราบว่า Web User ใดที่สั่งให้ httpd ทำงาน ด้วยคำสั่ง

     ps aux |grep http

     ผลคือ

     nobody   31159  0.0  1.5  29056 15588 ?        S    Dec17   0:00 /opt
    /lampp/bin/httpd -k start -DSSL -DPHP5

     จึงทราบว่า Web User ใช้ชื่อว่า ‘noboby’ (จากที่เคยคุ้นชินกับ www-data, apache อะไรทำนองนั้น)

     3. ตรวจสอบ Process อย่างละเอียดด้วยคำสั่งต่อไปนี้

     ps auxwe

     ผลที่ได้ พบว่า มี Process ของ httpd ทั่วๆไป จะแสดงรายละเอียดอย่างนี้

    nobody     3460  0.0  1.3  28060 14348 ?        S    Dec01   0:00 /op
    t/lampp/bin/httpd -k start -DSSL -DPHP5 LESSOPEN=| /usr/bin/lesspipe 
    %s USER=root MAIL=/var/mail/root SHLVL=4 LD_LIBRARY_PATH=/opt/lampp/li
    b:/opt/lampp/lib:/opt/lampp/lib: HOME=/root LOGNAME=root _=/opt/lampp/
    bin/apachectl TERM=vt100 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin
    :/usr/bin:/sbin:/bin LANG=en_US.UTF-8 LS_COLORS=no=00:fi=00:di=01;34:l
    n=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01
    :su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.t
    gz=01;31:*.svgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31
    :*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.b
    z=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.
    rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*
    .jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;3
    5:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=0
    1;35:*.png=01;35:*.svg=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg
    =01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.
    m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:
    *.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;3
    5:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.aac=00;3
    6:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=0
    0;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36: SHELL=/bin/bash L
    ESSCLOSE=/usr/bin/lesspipe %s %s PWD=/root

    แต่ พบว่า มีอยู่รายการหนึ่ง แสดงผลอย่างนี้

     nobody    5106  0.0  0.2   4168  2184 ?        S    Nov21   1:17 /usr
    /local/apache/bin/httpd -DSSL                                         
    
                                              -m a.txt HOME=/nonexistent O
    LDPWD=/var/spool/cron LOGNAME=nobody PATH=/usr/bin:/bin SHELL=/bin/sh 
    PWD=/home/wwwroot/experience/images/smilies/.laknat/.libs

     จึงตรวจสอบ Process PID 5106 ด้วยคำสั่ง

     ls -la /proc/5106

     ผลที่ได้คือ

     ซึ่ง จะเห็นได้ว่า Process นี้ สั่งทำงานจาก /home/wwwroot/experience/images/smilies/.laknat/.libs/httpd

    แต่ ก่อนหน้านี้ ผู้ดูแลระบบ ได้ สำรองข้อมูลออกไป แล้วลบทิ้งไปก่อนแล้ว จึงขึ้นคำว่า (deleted)

     จาก วิธีตรวจสอบเว็บไซต์ที่โดน Hack #6 พบว่า Hacker มักจะเขียน crontabs เอาไว้ เรียก Backdoor กลับมาอีก จึงทำการตรวจสอบที่ /var/spool/cron/crontabs ด้วยคำสั่ง

     ls -l /var/spool/cron/crontabs/

    ผลที่ได้คือ

    -rw------- 1 nobody crontab 271 2013-11-21 21:45 nobody
    -rw------- 1 root   crontab 256 2011-12-30 09:46 root

     และเมื่อ cat ออกมาดู พบว่า

    cat /var/spool/cron/crontabs/root
    
    # DO NOT EDIT THIS FILE - edit the master and reinstall.
    # (/tmp/crontab.WBj4te/crontab installed on Fri Dec 30 09:46:13 2011)
    # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
    # m h  dom mon dow   command
    0 3 * * * sh /backup.sh
    cat /var/spool/cron/crontabs/nobody
    
    # DO NOT EDIT THIS FILE - edit the master and reinstall.
    # (a.txt.d installed on Thu Nov 21 21:45:40 2013)
    # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
    * * * * * /home/wwwroot/experience/images/smilies/.laknat/.libs/a.txt.upd >/dev/null 2>&1

     แสดงให้เห็นว่า มี crontabs ของ nobody สร้างเมื่อเวลา Thu Nov 21 21:45:40 2013

    4.เครื่องนี้ใช้ lampp เป็น Web Server ซึ่ง ใช้พื้นที่ทำงานคือ

     /opt/lampp

     โดย ให้ผู้ใช้แต่ละคน สร้าง Web ในพื้นที่ /home ของแต่ละคนได้

     และเก็บ Logs ที่

     /opt/lampp/logs

     จึงตรวจสอบด้วยคำสั่ง

     grep "21/Nov/2013:21:45" /opt/lampp/logs/access_log

     ผลที่ได้คือ

    03-logplacefile

    แสดงให้เห็นว่า มีการเรียกไฟล์ *.php ใน images/stories ซึ่ง น่าจะเป็นช่องโหว่ จาก JCE Exploited ตาม วิธีตรวจสอบเว็บไซต์ที่โดน Hack #4 ซึ่งจะใช้วิธีการตามที่อธิบายไว้ก่อนหน้านี้แล้ว เพื่อตรวจสอบต่อไป

    5. เนื่องจาก ผู้ดูแลระบบ ได้สำรองข้อมูลของ /home/wwwroot/experience/images/smilies/.laknat/.libs/ เอาไว้ จึง เรียกออกมาดู

    ได้ผลดังนี้

    จะเห็นได้ว่า ไฟล์ a.txt.upd ถูกสร้างเมื่อเวลา 2013-11-21 21:45 จริงๆ

    เมื่อใช้คำสั่ง

    cat a.txt.upd

     ได้ผลว่า

    a.txt.upd

    จึงลองตรวจสอบ a.txt.run ด้วยคำสั่ง

    cat a.txt.run

     ได้ผลว่า

     a.txt.run

    และใช้คำสั่ง

    cat a.txt

     ซึ่งเป็นโปรแกรม ภาษา TCL ซึ่งมีรายละเอียดยาวมาก แต่ มีส่วนหนึ่ง เขียนว่า

    a.txt

    และจากการตรวจสอบ ทั้ง directory ก็พบว่า เป็นการเอา Network Tools ต่างๆ ได้แก่ Sniffer, Network Scanner และ อื่นๆอีกมากมาย ซึ่ง เอาตัวโปรแกรม เช่น TCL แบบ Portable มาด้วย หมายความว่า แม้เครื่อง Server ไม่ติดตั้ง TCL ก็สามารถทำงานได้เลยทีเดียว

     ดังนั้น เครื่องนี้ ถูกสั่งงานจากทางไกล กลายเป็น Botnet เพื่อตรวจสอบ เครื่องแม่ข่ายภายใน แม้มหาวิทยาลัยจะมี Firewall ป้องกัน แต่ถูกเครื่องนี้ ดักเก็บข้อมูล และแสดงผลกลับไปให้ Hacker ผ่านทาง Port TCP/80 ซึ่ง Firewall เปิดให้ใช้งานได้เลย

     5. ตรวจสอบว่า มีไฟล์ *.php ใน directory images/stories อีกหรือไม่ ด้วยคำสั่ง

     find /home -name "*.php" -type f | grep 'images/stories'

     ก็พบเพียงไฟล์เดียว คือ

     /home/wwwroot/research_old/images/stories/gh.php

     ซึ่งผิดสังเกต เมื่อตรวจสอบไฟล์ที่สร้างขึ้นในเวลาใกล้เคียงกัน ก็ไม่พบความผิดปรกติ ซึ่ง ไม่ปรกติ

    6. จึงตรวจสอบทั้ง /home ทุกไฟล์ *.php ที่อาจจะมี Backdoor ที่อาจซ่อนการใช้ฟังก์ชั่น eval หรือไม่ ด้วยคำสั่ง

    for f in $(find /home/ -name "*.php" -type f) ; do
      echo $f
      echo "---"
      grep 'eval(' "$f"
      echo "---"
    done

     พบว่า มีไฟล์ *.php ทั้งหมดจำนวน 15,883 ไฟล์ ในนั้นมี 200 กว่าไฟล์ ที่มีการใช้ฟังก์ชั่น eval จริง แต่บางส่วน ก็เป็นไฟล์ที่ถูกต้อง แต่มี 34 ไฟล์ ที่ เป็นไฟล์ Backdoor ใหม่ๆ เพิ่งสร้างขึ้นมา เช่น

    และ เป็นไฟล์ของระบบ ที่มีการแทรก Backdoor Code เข้าไป เช่น

    จึง เก็บรายชื่อไฟล์ทั้ง 34 นี้ ไว้ในไฟล์ ชื่อ 11-manualhack.txt และใช้คำสั่งต่อไปนี้ เพื่อเก็บไฟล์เอาไว้ เพื่อใช้ตรวจสอบต่อไป

     cat 11-manualhack.txt | xargs tar -cvf evalbackdoor.tar

     แล้วจึง ลบทิ้งด้วยคำสั่งต่อไปนี้

     cat 11-manualhack.txt | xargs rm -rf

     7. ตรวจสอบต่อไปว่า มี directory ใดบ้าง ที่ เปิดให้ Web User ‘nobody’ เขียนได้ ด้วยคำสั่ง

     find /home -user nobody -perm -u+w -type d

    พบว่า มี directory จำนวนมากที่เปิดให้ Web User เขียนได้

    และ ใช้คำสั่งต่อไปนี้ ดูว่า มี directory ใดบ้าง เปิดให้ใครๆก็เขียนได้ (World Writable) หรือ ตั้ง permission 777 ด้วยคำสั่ง

    find /home  -perm -o=w -type d

     ก็มีจำนวนมากเช่นกัน

     การแก้ไขปัญหา

    1. เก็บไฟล์ Backdoor ด้วยคำสั่งต่างๆข้างต้น และลบทิ้ง

    2. ปรับให้ทุก Directory เป็น Permission 755 ด้วยคำสั่ง

     find /home -type d -print0 | xargs -0 chmod 0755

     3. ปรับให้ทุก File เป็น Permission 644

     find /home -type f -print0 | xargs -0 chmod 0644

     4. เปลี่ยน Owner และ Group ของทุก Directory และ Files ให้เป็นของแต่ละ User ด้วยคำสั่งประมาณนี้

    chown -R user01.user01 /home/wwwroot

    5. ลบ crontab ด้วยคำสั่ง

    rm /var/spool/cron/crontabs/nobody

    6. หยุด Process PID 5106

    kill -9 5106

     คำแนะนำ

    เนื่องจาก ขณะนี้ได้ เราได้แต่ค้นหา ช่องโหว่ ตามที่เคยเรียนรู้มาเท่านั้น ยังมีรูปแบบต่างๆ ที่ยังไม่รู้อีกมากมาย จึงแนะนำให้

    1. ติดตั้ง OS ใหม่

    2. ติดตั้ง Joomla ใหม่ และ ย้ายเฉพาะ ข้อมูลที่อยู่ใน MySQL มา แล้วจึง Upgrade ให้เป็นรุ่นล่าสุด

    3. ย้ายเฉพาะ ภาพ และ ไฟล์เอกสารสำคัญมา แต่ต้องไม่เอาไฟล์ .php หรือ อื่นๆมาเด็ดขาด

    4. เข้มงวดกับการตั้ง Owner และ Permission กับผู้ใช้งานทุกคนของระบบ, หากจะมี Directory ใดต้องให้มีการ Upload ไฟล์ได้ จะต้องตั้งค่าไม่ให้ PHP ทำงานได้เด็ดขาด

    5. การใช้งาน Joomla จะต้องยกเลิกการ Upload ภาพผ่านทาง HTTP แต่ให้ใช้ FTP แทน

     และ บทความต่อๆไป จะพูดถึงการ Hardening เพื่อลดความเสียหายต่อไป

     ขอให้โชคดี

  • hands on backup joomla website and restore

    เรื่อง hands on backup joomla website and restore นี้จัดเตรียมโดยใช้ Oracle VM VirtualBox เพื่อฝึกปฏิบัติเกี่ยวกับการ backup website ในตัวอย่างจะใช้ joomla web site ซึ่งการ backup คงจะมีหลายแบบตามชนิดของการตั้ง website ในกรณีนี้ก็จะเป็นแบบตั้ง server เองแล้วใช้ apache web server + mysql server ที่ติดตั้งเอง

    สภาพแวดล้อมที่เตรียมมีดังนี้
    ubuntu router: มี Fix IP Address 10.0.100.1 (แจก DHCP IP Address ให้แก่ server1, server2 และ client)
    server1: 10.0.100.206 (joomla website รันบน apache web server)
    server2: 10.0.100.207 (backup)
    client: 10.0.100.205 (เพื่อใช้งาน ssh ผ่านทาง terminal และเพื่อใช้งาน firefox web browser)

    KM4-backup-diagram

    รูปภาพระบบทดสอบนี้

     

    การเตรียม VM
    ดาวน์โหลด [ ubunturouter-dhcp.ova  1.1GB ]  [ ubuntuserver-lamp.ova  1.2GB ] [ linuxmint15-live.ova 81KB]  [ linuxmint-15-mate-dvd-32bit.iso  1.0GB ] หรือได้รับแผ่น DVD ไฟล์ทั้งหมดนี้
    แล้วทำดังนี้
    1. ทำ import ไฟล์ ubunturouter-dhcp.ova ตั้งชื่อว่า ubuntu router ตั้ง network adapter1: NAT, ตั้ง network adapter2: internal network (ตั้ง name: intnet1)
    2. ทำ import ไฟล์ ubuntuserver-lamp.ova ตั้งชื่อว่า server1 ตั้ง network adapter1: internal network (ตั้ง name: intnet1)
    3. ทำ import ไฟล์ ubuntuserver-lamp.ova อีกครั้ง ตั้งชื่อว่า server2 ตั้ง network adapter1: internal network (ตั้ง name: intnet1)
    4. ทำ import ไฟล์ linuxmint15-live.ova ตั้งชื่อว่า client ตั้ง network adapter1: internal network (ตั้ง name: intnet1) และตั้งค่า Storage ใช้ CD/DVD ด้วยไฟล์ linuxmint-15-mate-dvd-32bit.iso

    หมายเหตุ เครื่อง 1,2 และ 3 เปิดเครื่องเข้าใช้ด้วย login mama / password 123456

    การเตรียมทำ joomla site ทดสอบ
    1. สร้าง database ที่ต้องการติดตั้ง ตัวอย่าง สร้าง database ชื่อ testdatabase ใช้คำสั่ง

    mysql -uroot -p123456 -e "CREATE DATABASE testdatabase CHARACTER SET 'UTF8';"

    2. กำหนดสิทธิ์การใช้ database testdatabase ให้แก่ user ตัวอย่างสร้าง user ชื่อ mamamysql โดยมีรหัสผ่านว่า mamapass ใช้คำสั่ง

    mysql -uroot -p123456 -e "grant all privileges on testdatabase.* to 'mamamysql'@'localhost' identified by 'mamapass' ;"

    3. เตรียมพื้นที่สำหรับติดตั้ง joomla ไว้ที่ /var/www/testjoomla ด้วยคำสั่ง

    sudo mkdir -p /var/www/testjoomla

    4. ดาวน์โหลดแฟ้ม joomla 2.5.9 มาเก็บไว้ด้วยคำสั่ง

    wget http://ftp.psu.ac.th/pub/joomla/Joomla_2.5.9-Stable-Full_Package.tar.gz -P /tmp

    แล้วแตกแฟ้มออกมาเก็บไว้ที่ /var/www/testjoomla ด้วยคำสั่ง

    sudo tar -zxvf /tmp/Joomla_2.5.9-Stable-Full_Package.tar.gz -C /var/www/testjoomla

    5. แล้วปรับสิทธิ์เจ้าของ /var/www/testjoomla ให้แก่ apache ด้วยคำสั่ง

    sudo chown -R www-data.www-data /var/www/testjoomla

    6. ต่อไปต้องติดตั้งปรับแต่งระบบ joomla ครั้งแรก เปิด browser (firefox, chrome, Internet Explorer ฯลฯ) แล้วพิมพ์

    http://10.0.100.206/testjoomla

    (หมายเหตุ 10.0.100.206 คือ ip address server ที่ติดตั้ง Joomla)

    ขั้นตอน 1 : Choose language (เลือกภาษา) ให้ click ปุ่ม Next (หรือต่อไป)
    ขั้นตอน 2 : Pre-Installation Check(ตรวจสอบระบบก่อนติดตั้ง) ให้ click ปุ่ม Next (หรือต่อไป)
    ขั้นตอน 3 : License(ลิขสิทธิ์) ให้ click ปุ่ม Next (หรือต่อไป)
    ขั้นตอน 4 : Database Configuration(การตั้งค่าฐานข้อมูล) ให้ใส่ค่าดังนี้ (อย่าลืมใส่ข้อมูลชิดซ้าย ห้ามมีช่องว่าง)
    Database type = mysqli(เลือกที่มี i) (ความแตกต่างระหว่าง mysqli และ mysql)
    Host Name = localhost
    Username = mamamysql
    Password = mamapass
    Database Name = testdatabase เสร็จแล้ว click Next (หรือต่อไป)
    ขั้นตอน 5 : FTP Configuration (ตั้งค่า FTP) ให้ click ปุ่ม Next (หรือต่อไป)
    ขั้นตอน 6 : Main Configuration (ตั้งค่าหลักของ เว็บ) ให้ใส่ค่าดังนี้
    Admin Username = ไม่ต้องแก้ไข ทิ้งไว้เหมือนเดิมคือ admin
    Admin Password = 123456
    Confirm Admin Password = 123456
    อย่าลืม ต้อง Click Install Sample Data (ติดตั้งข้อมูลตัวอย่าง) แล้วกด Next (หรือ ต่อไป)
    ขั้นตอนที่ 7 : Finish (เสร็จสิ้น) (ขั้นตอนสุดท้าย)

    7. แล้วปรับสิทธิ์เจ้าของ /var/www/testjoomla ให้แก่ user ที่ไม่ใช่ www-data ด้วยคำสั่ง

    sudo chown -R mama.mama /var/www/testjoomla

     

    การตั้งค่าการสำรองข้อมูลข้ามเครื่องโดยไม่ต้องถามรหัสผ่าน

    1. ใช้คำสั่งดังนี้คือ

    ssh-keygen -t dsa

    ได้ผลข้อความตัวอย่างว่า
    Generating public/private dsa key pair.
    Enter file in which to save the key (/home/User/.ssh/id_dsa):
    ให้ป้อน Enter จะได้ข้อความตัวอย่างว่า
    Enter passphrase (empty for no passphrase):
    ให้ป้อน Enter จะได้ข้อความตัวอย่างว่า
    Enter same passphrase again:
    ให้ป้อน Enter จะได้ข้อความตัวอย่างว่า
    Your identification has been saved in /home/User/.ssh/id_dsa.
    Your public key has been saved in /home/User/.ssh/id_dsa.pub.
    The key fingerprint is:
    66:ce:cc:8d:33:0d:49:9a:05:26:77:95:1a:3e:96:5f User1@Server1
    ถึงตอนนี้ ระบบจะสร้างแฟ้ม ~/.ssh/id_dsa และ ~/.ssh/id_dsa.pub ขึ้นมา

    2. ให้นำแฟ้ม ~/.ssh/id_dsa.pub จากเครื่องนี้ ไปเก็บไว้ที่ home ของ User2 ปลายทาง @server2
    ด้วยตัวอย่างคำสั่งว่า ssh-copy-id -i ~/.ssh/id_dsa.pub User2@Server2 เช่น

    ssh-copy-id -i ~/.ssh/id_dsa.pub mama@10.0.100.207

    อาจมีการถามรหัสผ่านของ User2@Server2 ให้ป้อนรหัสผ่านให้ถูกด้วย
    หากสำเร็จถูกต้อง ข้อมูลจะถูกเก็บที่ home ของ User2 ปลายทาง @server2 ในแฟ้ม ~/.ssh/authorized_keys

    3. เพื่อความปลอดภัย ให้กำหนดสิทธิ์เฉพาะเจ้าของจึงจะอ่านแฟ้มเหล่านีได้ ด้วยตัวอย่างคำสั่ง

    chmod 600 ~/.ssh/id_dsa ~/.ssh/id_dsa.pub

     

    การตั้งค่า cron เพื่อ backup server1 (10.0.100.206) ไปยัง server2 (10.0.100.207)

    1. ขั้นตอนนี้ต้องเข้าเป็น username root ด้วยคำสั่ง

    sudo su -

    2. สร้างแฟ้ม /etc/cron.d/cron-backup

    nano /etc/cron.d/cron-backup

    ใส่คำสั่งดังนี้

    0 4 * * * root sh /root/backup-joomla.sh

    3. สร้างแฟ้ม /root/mydaily.sh ด้วยคำสั่ง

    nano /root/backup-joomla.sh

    มีข้อมูลคำสั่งต่างๆที่ต้องการสั่งให้ทำงาน ดังนี้

      #!/bin/bash
      TODAY=$(date "+%Y%m%d%H%M")
      mysqldump -u mamamysql -pmamapass testdatabase > /root/testjoomla_${TODAY}.sql
      scp -i /home/mama/.ssh/id_dsa /root/testjoomla_${TODAY}.sql mama@10.0.100.207:backup_testjoomla
      tar -zcp -f /root/testjoomla_${TODAY}.tgz -C /var/www/testjoomla .
      scp -i /home/mama/.ssh/id_dsa /root/testjoomla_${TODAY}.tgz mama@10.0.100.207:backup_testjoomla

    4. เตรียมเสร็จ ให้ลองทดสอบระบบ

    login เข้าไปที่ server2 ด้วย ssh เพื่อสร้างไดเรกทอรี

    ssh -i /home/mama/.ssh/id_dsa mama@10.0.100.207 "mkdir backup_testjoomla"

    ทดสอบ run script นี้สัก 1 ครั้ง

    sh /root/backup-joomla.sh

    ตรวจสอบดูว่ามีการส่งไฟล์ไปเก็บจริงไม๊

    ssh -i /home/mama/.ssh/id_dsa mama@10.0.100.207 "ls -l backup_testjoomla"

    5. เสร็จแล้วสั่งให้ cron ทำงานใหม่ด้วยคำสั่ง

    service cron restart

    6. ออกจาก sudo su ด้วยคำสั่ง

    exit

     

    การ restore joomla web site

    1. สร้าง database ที่ต้องการติดตั้ง ตัวอย่าง สร้าง database ชื่อ testdatabase ใช้คำสั่ง

    mysql -uroot -p123456 -e "CREATE DATABASE testdatabase CHARACTER SET 'UTF8';"

    2. กำหนดสิทธิ์การใช้ database testdatabase ให้แก่ user ตัวอย่างสร้าง user ชื่อ mamamysql โดยมีรหัสผ่านว่า mamapass ใช้คำสั่ง

    mysql -uroot -p123456 -e "grant all privileges on testdatabase.* to 'mamamysql'@'localhost' identified by 'mamapass' ;"

    3. เตรียมพื้นที่สำหรับติดตั้ง joomla ไว้ที่ /var/www/testjoomla ด้วยคำสั่ง

    sudo mkdir -p /var/www/testjoomla

    4. แตกแฟ้มที่ backup นั้น (testjoomla_201309031231.tgz) ออกมาเก็บไว้ที่ /var/www/testjoomla ด้วยคำสั่ง

    sudo tar -zxvf backup_testjoomla/testjoomla_201309031231.tgz -C /var/www/testjoomla/

    5. import ข้อมูล เข้า ด้วยคำสั่ง

    mysql -uroot -p123456 testdatabase < backup_testjoomla/testjoomla_201309031231.sql

    6. แล้วปรับสิทธิ์เจ้าของ /var/www/testjoomla ให้แก่ user ที่ไม่ใช่ www-data ด้วยคำสั่ง

    sudo chown -R mama.mama /var/www/testjoomla

    7. ก็จะสามารถเข้า server2 ได้ที่ http://10.0.100.207/testjoomla

  • Check previous running script using process id

    วันนี้ในกลุ่ม sysadmin บน facebook คุยกันเรื่องของ ubuntu mirror ของ PSU แล้วมีประเด็นของการใช้ script สำหรับการ mirror

    ตัว script ที่ว่านี้ จะเป็น shell script ธรรมดา ที่จะไปเรียกใช้โปรแกรม rsync ที่จะทำหน้าที่ mirror ข้อมูลจาก primary ftp/rsync site ของ ubuntu มาเก็บไว้ที่เครื่อง server ของ PSU ตัว script จะถูกเรียกใช้โดย cron กำหนดไว้ใน crontab และเรียกใช้ 4-6 ครั้งใน 1 วัน (หรือ run ทุกๆ 6 หรือ 4 ชั่วโมง) ระยะเวลาที่ใช้ในการ run script ไม่แน่นอนว่าจะใช้เวลาเท่าไหร่ ขึ้นอยู่กับปริมาณของข้อมูลที่จะต้อง mirror จากต้นทางมายังปลายทาง

    ถึงแม้ว่าการใช้ rsync จะช่วยให้ transfer เฉพาะไฟล์ ที่มีการเปลี่ยนแปลงไป หลังจากการ mirror ครั้งสุดท้ายมา แต่ในบางครั้งจำนวนของข้อมูลที่เปลี่ยนแปลงไป ก็อาจจะมากกว่าปกติ เช่นในช่วงเวลาที่มีการเปลี่ยน release ซึ่งทำให้มีไฟล์ใหม่ๆ เพิ่มขึ้นเป็นจำนวนมาก ทำให้ จากช่วงปกติ ซึ่งอาจจะใช้เวลาเพียง 1-2 ชม. ในการ mirror และ mirror ทุกๆ 4 ชม. ก็เพียงพอ ในช่วงเวลาของการปรับเปลี่ยน release ถ้าจะ mirror ให้เสร็จ ก็อาจจะใช้เวลาถึง 12 ชม. เป็นต้น

    จะเกิดอะไรขึ้น ถ้าในการ mirror ครั้งที่แล้ว ยังไม่เสร็จสิ้นสมบูรณ์ แล้วก็ถึงเวลาของการ mirror รอบถัดไป?

    ถ้าไม่มีการตรวจสอบใดๆเลย กระบวนการของการ mirror ในครั้งถัดมาก็จะเริ่มทำงาน ในขณะที่รอบแรกยังไม่เสร็จ และ ถ้ามีข้อมูลที่ถูกเปลี่ยนแปลงมากจริงๆ ถึงรอบที่ 4-5 ของการ mirror แล้ว … การ mirror ครั้งแรกก็ยังไม่เสร็จเรียบร้อยดี … และพอถึงขั้นนี้แล้ว ระบบจะมีภาระงานสะสมมากขึ้นเรื่อยๆ และระบบเริ่มช้าลง และ จำนวน process ของการ mirror ที่คั้งค้างอยู่ก็จะเพิ่มขึ้นเรื่อยๆ เพราะ process หลังๆ ก็จะไปหน่วงการทำงานของ process แรกๆให้ทำงานช้าลงไปด้วย

    ครั้งแรกที่ผมเจอปัญหาในลักษณะนี้ process ของการ mirror ที่ run ค้างอยู่ทำให้ load ของระบบสูงกว่า 100 โชคยังดีที่ load ที่สูงขึ้นเกิดจาก i/o ของการเขียน disk ซึ่งยังทำให้สามารถ secure shell เข้าไปได้ สามารถ run คำสั่ง ps auxw เพื่อตรวจสอบได้ ถึงแม้จะช้าอยู่มาก แต่ก็ทำให้ทราบว่าปัญหาเกิดจากอะไร และเอาข้อมูลนั้นมาแก้ไขปัญหาในภายหลังได้

    สำหรับปัญหาแบบนี้ วิธีการแก้ไข ก็ไม่ได้ยากอะไร การทำงานของ mirror process ในครั้งหลังที่ถูก start ขึ้นมาด้วย cron ไม่ควรที่จะทำงานต่อ ถ้า process แรกที่ทำงานอยู่ ยังทำงานไม่เสร็จ ควรที่จะปล่อยให้ process แรกทำงานให้เสร็จเท่านั้นเอง

    ในแง่ของ shell script ก็สามารถทำได้ โดยการใช้ lock file ก่อนที่จะเริ่มต้นทำงาน ก็ตรวจสอบดูก่อนว่า มี lock file อยู่หรือเปล่า ถ้ามี ก็แสดงว่ายังมี process เดิมทำงานอยู่ ไม่ต้องทำอะไร ให้ terminate ไป ถ้าไม่มี lock file ก่อนที่จะเริ่มต้นทำงาน ก็สร้าง lock file ขึ้นมา เพื่อบอกว่า กำลังทำงานนี้อยู่ หลังจากทำงานเสร็จแล้ว ก็ลบ lock file นั้นทิ้ง เพื่อบอกว่า process ทำงานเสร็จแล้ว

    เขียนเป็น script คร่าวๆ ได้ประมาณนี้ครับ

    LOCK="/tmp/script-name.lock"
    if [ -f "$LOCK" ]; then
        # lock file exist
        echo "Previous process is still running..."
        exit  -1 # Terminate script here
    fi
    
    # No other process, we do our job as usual
    
    ...
    
    # end of our duty, do cleaning up, remove lock file
    
    rm -f $LOCK
    
    exit 0  # Terminate normally

    วิธีการนี้ เป็นวิธีการที่ใช้อยู่แล้ว สำหรับ mirror script ของ ubuntu แต่ล่าสุดนี้ มีปัญหาเกิดขึ้นก็คือ process ของการ mirror ที่ควรจะ run เป็นระยะๆ กลับหยุดทำงานไปหลายวัน

    ปัญหาที่เกิดขึ้นก็คือ script ที่ cron เรียกให้ทำงานนั้น ไม่ได้ทำงานเสร็จ ตามปกติของมัน ซึ่งจะมีการลบ lock file ทิ้งไป ซึ่ง สาเหตุอาจจะเกิดขึ้นได้จากหลายๆกรณี ในส่วนที่ผมเคยเจอ ก็คือ “ไฟดับ” ระบบที่ผมดูแลอยู่ในตอนนั้นถึงจะมี UPS backup แต่ไม่มีส่วนของการ monitor UPS และควบคุมให้ shutdown เครื่องแบบอัตโนมัติ ถ้าเกิดไฟดับนานเกินไป และ battery ของ UPS ไม่สามารถจ่ายไฟฟ้าให้นานพอจนกระทั่งไฟฟ้ากลับมาเป็นปกติได้ ตัว script ก็ตายไปกลางคัน พร้อมๆกับเครื่อง และ lock file นั้นก็ไม่ได้ถูกลบไป ทำให้ script ที่ run โดย cron ในรอบถัดไปไม่สามารถทำงานในส่วนของการ mirror ได้ เพราะ lock file ยังอยู่ (ตัว lock file ไม่ได้เก็บไว้ใน /tmp หรือ /var/lock) ส่วนกรณีอื่นๆ ก็จะเป็นกรณีที่ script เกิดตายไปกระทันหันโดยสาเหตุอื่นๆ เช่น out of memory หรือ process ถูก kill โดยกรณีอื่นๆ และ lock file ก็ถูกทิ้งค้างเอาไว้

    ตัว script ที่ผมใช้ในสมัยหลัง ก็เลยมีส่วนของการตรวจสอบเพิ่มขึ้นมาอีกอย่างหนึ่ง ก็คือ นอกจากจะ สร้าง lock file แล้ว ก็จะเก็บ process id ของ script เอาไว้ใน lock file นั้นด้วย ในการทำงานของ script ส่วนของการตรวจสอบว่า process ของ script ที่ทำงานอยู่ก่อนหน้านี้ ยังทำงานอยู่หรือเปล่า นอกจากตรวจสอบว่ามี lock file แล้ว ก็จะตรวจสอบว่า process ของ lock file นั้น ยังมีอยู่ในระบบหรือเปล่า

    ส่วนของการเก็บ process id ของ script นั้น ใช้วิธีการ

        echo "$$" > $LOCK

    ได้เลยตัว special shell variable ‘$$’ จะเป็นหมายเลข process id ของ shell ที่ใช้ในการ run script ตัวนั้น

    ส่วนการตรวจสอบว่า process นั้นยังทำงานอยู่หรือเปล่า ก็ใช้ process id ที่เก็บอยู่ใน lock file เอามาตรวจสอบ ซึ่งเราอาจจะตรวจสอบ โดยการใช้คำสั่ง

        ps ax | grep $previous_pid | grep -v grep

    ดูก็ได้ แต่จริงแล้ว ก็มีวิธีง่ายกว่านั้น ก็คือ สำหรับ Linux แล้ว จะมี virtual directory ที่ชื่อว่า /proc และ ทุกๆ process จะถูกสร้างเป็น sub directory ใน /proc โดยใช้ชื่อเป็นหมายเลขของ process id ดังนั้น แทนที่เราจะตรวจสอบโดยการใช้คำสั่ง ps ซึ่งจะต้องส่ง ourput ไปให้คำสั่ง grep อีก 2 รอบ ก็สามารถตรวจสอบเพียงแค่ว่ามี directory นั้นอยู่หรือเปล่าได้เลย โดยการใช้

        if [ -d /proc/$previous_pid ]; then
        ...
        fi

    ตัวอย่าง script ที่ใช้งานวิธีการนี้

    #!/bin/sh
    TASKNAME="this"
    LOCK="/tmp/${TASKNAME}.LCK"
    LOG="/tmp/${TASKNAME}.log"
    
    lock() {
        if [ -f $LOCK ]; then
           D=`date`
           task_pid=`cat $LOCK`
           if [ -d "/proc/$task_pid" ]; then
              # it's possible that this_pid is different task, but
              # it is very unlikely.
              echo "$D : Previous process (pid: $task_pid) is running"
              exit
           else
              # Lock is not clean up properly, assume
              echo "$D : clean up previous lock file (pid: $task_pid)"
           fi
        fi
        echo $$ > $LOCK
    }
    
    unlock() {
        rm -f $LOCK
    }
    
    do_myjob() {
        START=`date`
        sleep 10    # This is the real 'task' of this script
        STOP=`date`
        echo "Process start at: $START" >> $LOG
        echo "Process stop at : $STOP"  >> $LOG
    }
    
    lock
    do_myjob
    unlock

    ใน script ตัวอย่างข้างต้น สมมติ save ให้อยู่ในชื่อว่า t และกำหนด permission ให้สามารถ execute ได้ โดยใช้

    $ editor t
    $ chmod +x t

    เราสามารถ ตรวจสอบการทำงานของ script ดูได้ โดย ทดลอง run

    $ ./t

    ซึ่งมันจะรอเวลา 10 วินาที ตาม “sleep 10” ในฟังก์ชัน do_myjob() ก่อนที่จะกลับมาที่ shell prompt อีกครั้งนึง และในไฟล์ /tmp/this.log จะแสดง เวลาเริ่มต้นทำงาน และ ทำงานเสร็จ
    ถ้า run script ด้วยคำสั่ง

    $ ./t & ./t

    ตัว script ตัวแรกจะทำงานตามปกติ แต่ตัวที่สอง จะแสดงข้อความว่า “…Previous process (pid: xxxxx) is running” และ เราจะไม่สามารถ run script ครั้งที่สองได้ จนกว่า process ที่ run ค้างอยู่ทำงานเสร็จแล้ว และ ถ้าตรวจสอบใน log file ก็จะเห็นว่า การทำงานของ script จะเป็น [start, stop], [start, stop] ไปเรื่อยๆ ไม่มีการซ้อนเหลือมกัน

    แต่ใน script ตัวนี้ ถ้าหาไป comment บรรทัด lock; do_myjob; unlock เป็น

    # lock
    do_myjob
    # unlock

    แล้วทดลอง run script

    $  ./t & ./t

    ก็จะเห็นว่า script สามารถ run ซ้อนกันได้ และ ใน log file ก็จะเห็นเป็น star, stat, stop, stop ซ้อนเหลื่อมกัน ซึ่งเป็นกรณีที่เราไม่ต้องการให้เกิดขึ้น

    script ตัวนี้ อาจจะไม่ได้สมบูรณ์ 100% ในการใช้การตรวจสอบว่า มี process เดิมยังทำงานค้างอยู่หรือเปล่า เพราะมีความเห็นไปได้ว่า process id ที่มีอยู่และตรวจสอบจาก /proc ได้นั้น ไม่ได้เป็น process ของ script ตัวนี้ ที่ทำงานไปในครั้งที่แล้ว และยังทำงานไม่เสร็จ แต่เป็น process อื่นๆ ซึ่งไม่เกี่ยวข้องกับ script ตัวนี้เลยก็เป็นไปได้ เพราะ process id จะมีการเอากลับมาใช้ใหม่ หลังจากใช้ไปจนครบแล้ว แต่ โอกาสเช่นนั้น จะเกิดขึ้นได้น้อยมาก (process id เป็น process id เดียวกับ script ที่ terminate ไปแล้วแบบผิดปกติ และ process ที่ไม่เกี่ยวข้องทำงานอยู่ในช่วงเวลานี้พอดี) การตรวจสอบเพ่ิมเติม จะทำให้ script มีความซับซ้อนมากขึ้น โดยใช่เหตุ ก็เลยทิ้งไว้เท่านี้ครับ ผู้ที่สนใจ อาจจะตรวจสอบโดยใช้ ข้อมูลอื่นๆ ใน /proc/$process_id เช่น cmdline ดูได้ ทิ้งไว้ให้ไปลองทำเป็นการบ้านดูครับ 🙂

  • How to install LibreOffice 4.0 in Ubuntu & Mint

    1. Uninstall LibreOffice รุ่นเก่าออกให้หมด
      $sudo apt-get remove --purge libreoffice*
    2. Download Libreoffice for Ubuntu ที่ LibreOffice โดยเลือกรุ่นให้ถูกต้องว่า 64 บิตหรือ 32บิต (x86 หรือ x86_64) ดูได้จากคำสั่ง
      $uname -a
      หากได้ผลลัพธ์ว่า
      Linux Enterprise 3.5.0-23-generic #35-Ubuntu SMP Thu Jan 24 13:15:40 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
      แปลว่า 64บิต สังเกตุว่ามี x86_64 ต่อท้าย ส่วนรุ่น 32บิตจะได้ว่า
      Linux ubuntu 2.6.32-34-generic-pae #77-Ubuntu SMP Tue Sep 13 21:16:18 UTC 2011 i686 GNU/Linux
      สังเกตว่ามี i686 เครื่องรุ่นเก่ากว่านี้อาจจะเป็นเลขอื่นๆ

    (more…)

  • สร้าง log สำหรับ DNS Query เมื่อใช้ bind9 เป็น DNS Server

    เนื่องจากปัญหาของ DNS Query Brute Force Attack ที่เกิดขึ้นกับ DNS Server บางตัวภายในเครือข่ายของมหาวิทยาลัย สงขลานครินทร์ วิธีการแรกที่ควรจะเอามาใช้ในการที่จะแก้ปัญหานี้ก็คือ “identify your enemy”.

    ใครคือคนที่ส่งคำสั่ง query เข้ามา? query อะไร? และ query บ่อยขนาดใหน?
    หลังจากแยกแยะข้อมูลในส่วนนี้ได้แล้ว เราค่อยตัดสินใจกันต่อว่าจะทำอย่างไรต่อไป

    สำหรับบทความนี้ จะพูดถึง Bind9 เท่านั้น Bind version ต่ำกว่า 9 ยังไม่ได้ทดสอบว่าใช้งานได้หรือเปล่า ส่วน version สูงกว่า 9, ถ้ามีก็น่าจะใช้งานได้ ส่วน DNS Server ตัวอื่นๆ ก็ยังไม่ได้ใช้อย่างจริงจังก็เลยไม่รู้เหมือนกันครับว่าจะจัดการอย่างไร แต่โดยหลักๆแล้วก็เหมือนกันครับ ก็คือ

    สร้าง log แล้วตรวจสอบจาก log

    สำหรับ bind9 ในส่วนของ option configuration จะมี keyword “logging” อยู่ ซึ่งสามารถใช้ระบุการ log ข้อมูลแบบต่างๆได้หลายอย่าง และ หลายรูปแบบมาก ในที่นี้ สมมติ เราสนใจเฉพาะส่วนของการ query นะครับ ก็อาจจะสร้าง config ได้แบบนี้

    logging {
          channel query_logging {
                 file “/var/log/named/query.log”;
                 print-time yes;
          };
          category queries { query_logging; };
    };

    เป็นการสร้าง channel สำหรับ log ขึ้นมาชื่อว่า “query_logging” โดยข้อมูลที่ถูกส่งมาที่ channel นี้จะเก็บลงไว้ในไฟล์ที่ชื่อว่า query.log ซึ่งไฟล์นี้จะเก็บอยู่ใน directory ที่ชื่อว่า /var/log/named

    config ข้างต้นจะเก็บเอาไว้ในไฟล์ /etc/bind/named.conf.options (สำหรับ Debian หรือ Ubuntu)
    โดยเพิ่มต่อท้ายเข้าไป

    ในส่วนของ directory ที่ใช้ในการเก็บ log (/var/log/named) ถ้ายังไม่มีอยู่ ก็ต้องสร้างขึ้นมาใหม่ก่อน และกำหนดให้เป็นของ user ซึ่ง run ตัว named daemon ซึ่งสำหรับบน Debian/Ubuntu จะใช้ user ที่ชื่อว่า bind ดังนั้น สามารถสร้าง diectory ขึ้นมาใช้งานได้โดยใช้คำสั่งดังนี้

    $ sudo mkdir -p /var/log/named
    $ sudo chown bind:bind /var/log/named

    หลังจากแก้ไขไฟล์ /etc/bind/named.conf.options แล้ว เราก็ควรที่จะตรวจสอบก่อนว่า config ไฟล์ที่แก้ไขไปยังอยู่ใน format ที่ถูกต้องก่อนที่จะใช้งาน ซึ่งทำได้โดยการใช้คำสั่ง

    $ sudo named-checkconf /etc/bind/named.conf

    ซึ่งถ้าไม่มีข้อความใดๆแสดงออกมา ก็แสดงว่า config ถูกต้อง เราก็สามารถสั่งให้ named อ่าน config ใหม่เข้าไปใช้ได้ โดยการใช้คำสั่ง

    $ sudo service bind9 reload

    หรือ

    $ sudo rndc reconfig

    หลังจากนั้น ก็สามารถตรวจสอบการทำงานของตัว named server ได้โดยใช้คำสั่ง

    $ host www.mydomain.net localhost

    เปลี่ยน ‘www.mydomain.net’ เป็น host ที่ตัว name server ของเราให้บริการอยู่
    หลังจากนั้นก็ไปตรวจสอบดูว่าในไฟล์ /var/log/named/query.log มีข้อมูลเพิ่มขึ้นมาหรือไม่

    ซึ่งถ้าการ config ทุกอย่างถูกต้อง ก็ควรที่จะมีข้อมูลของการ query เกิดขึ้น
    ตัวอย่างของข้อมูลที่ตัว named จะ log เอาไว้ครับ

    28-Nov-2012 11:54:14.911 client 174.127.97.86#25345: query: isc.org IN ANY +ED (172.30.0.85)
    28-Nov-2012 11:54:15.589 client 174.127.97.86#25345: query: isc.org IN ANY +ED (172.30.0.85)

    ซึ่งในที่นี้ client ที่ส่งคำสั่ง query มาก็คือ 174.127.97.86 โดย server ก็คือ 172.30.0.85 และข้อมูลที่ query ก็คือ isc.org ชนิดของข้อมูลเป็น any (ข้อมูลทั้งหมดเท่าที่มี)

    ในสภาวะที่ตัว Server ของเราถูกโจมตี ขนาดของ query.log ก็จะเพิ่มขึ้นอย่างรวดเร็วมาก
    จากตัวอย่างข้างต้น query ทั้งสองครั้งเกิดขึ้นในช่วงเวลาน้อยกว่า 1 วินาที (ครั้งแรกเวลา 11:54:14, ครั้งถัดมา 11:54:15 ตัวเลข 3 ตัวหลังจุด จะเป็นส่วนย่อยของวินาที)

    เราจะรู้ได้อย่างไรว่า client ตัวใหนบ้างที่กำลังโจมตี server ของเราอยู่ ตัวใหนที่ใช้งานแบบปกติ?
    เราสามารถใช้เครื่องมือบน linux/unix ในการช่วยตรวจสอบได้ครับ โดยการนับ

    $ cat /var/log/named/query.log | cut -f4 -d’ ‘ | cut -f1 -d’#’ | sort | uniq -c | sort -rn | head -20

    ตัวอย่าง output ที่ผมได้จาก server ที่ถูกโจมตีคือ

      18174 178.32.76.101
      10924 64.120.228.188
       5297 194.8.75.57
       4584 37.77.82.161
       1744 72.20.55.30
       1548 88.232.24.58
       1356 216.119.157.90
       1242 69.64.58.160
       1189 5.47.41.41
       1155 128.204.201.22
       1115 5.135.60.245
        872 31.214.144.181
        629 37.59.93.86
        513 88.228.212.44
        467 94.121.242.57
        438 178.32.239.65
        382 37.59.194.200
        343 174.127.92.85
        325 174.127.103.234
        296 88.245.53.247

    ตัวเลขข้างหน้าคือจำนวนครั้ง และตัวเลขข้างหลังคือ ip address ของ client ที่ส่งคำสั่ง query เข้ามาทั้งหมดนี้ query ช้อมูลตัวเดียวกันก็คือ “isc.org” type ANY

    เรารู้ตัวว่าผู้โจมตีเป็นใครแล้ว หลังจากนี้ก็จะเป็นคำถามว่า แล้วจะแก้ปัญหาอย่างไร
    ขอยกไปเป็นบทความหน้าครับ