<?
/*
프로그램 : AR 해킹 체크

버전정보 : archeck 0.7  2017-10-17 (1달에 1~2회 업데이트)

기능설명 : 해당 서버의 하위디렉토리의 파일중 해킹파일 검색

최초개발 : 2017-10-16 김성대 xhost / freeimage.kr

업데이트 : http://freeimage.kr/site/archeck (비정기적)

저작권자 : 김성대 (actrun@actrun.com)

설치방법 : 1) 소스 상단에 $config[safe_url] 에 안전한 주소 지정할것
           2) 그누보드 또는 워드프레스의 최상위 경로에 넣고 실행함.

패턴갱신 : 1) 화면에 나오는 메시지를 바탕으로 직접 수정하는경우
           2) 배포사이트에 접속해서 패턴업데이트 요청하거나
		   3) 해킹인지 아닌지 의심가는경우 파일을 개발자에게 메일로 보내서 확인받는 방법 (해킹이 아닌것으로 판명되면 해당 패턴 업데이트함)

업데이트 방법 : 직접 입력하거나, 새로운 패턴을 다운받아서 입력함 (배포사이트에서 제공)

sir.kr 구매자를 위한 안내 :
   구매후 구매 확인 메일을 보낸 구매자에 한해 3개월간 검사 패턴 업데이트 무상 제공함 (3개월이후는 sir.kr에 올라온 신규 패턴 구매해야함)


감지내용 :
1) .htaccess 에 이상 url 검사
2) 소스내 eval 함수 검사
3) 미리 지정하지 않은 URL로 이동 header("location: 이동)
4) 파일크기에 비해 지나치게 많은 이상 함수,패턴 사용
   - $GLOBALS / \x31 같은 내용 함수 사용
5) $GLOBALS를 배열로 사용하는경우
6) data 폴더에 index.php 파일에 이상소스 포함
7) data 폴더에 php 소스 등록됨
8) 이미지인데 파일내용은 이미지가 아니다.. 넌 누구냐?


설치하거나 인스톨하는 방식이 아닙니다. ftp로 서버에 직접 넣어서 주소창에서 실행하는 방식이니 참고바랍니다.
(설치/자동 업데이트 방식은 차후 지원할 예정입니다.)

*/





if (!isset($config)) $config=array();

//빈칸으로 안심되는 사이트를 넣거나
$config['safe_url']=explode(' ','freeimage.kr 10min.co.kr');
//아래에 한줄씩 추가하세요.
$config['safe_url'][]='latu.co.kr';
$config['safe_url'][]='azo.co.kr';
$config['safe_url'][]=$_SERVER['HTTP_HOST'];
$config['safe_url'][]=str_replace('www.','',$_SERVER['HTTP_HOST']);

//1.현재 경로의 하위 모든 파일검색한다.
$config['show']=true;	//진행중인 상황을 보이게 할때 true;

//안전한 함수명들
$config['safe_code']=array();
$config['safe_code'][]='eval()';
$config['safe_code'][]='eval(n';
$config['safe_code'][]='system()';
$config['safe_code'][]="eval('";
$config['safe_code'][]='eval(&';
$config['safe_code'][]='curl_exec(';
$config['safe_code'][]='curl_exec (';
$config['safe_code'][]='exec($exe';
$config['safe_code'][]="exec('";
$config['safe_code'][]="exec()";
$config['safe_code'][]="exec( \$this->";
$config['safe_code'][]="exec(\$this->";
$config['safe_code'][]="_exec";
$config['safe_code'][]="doubleval";
$config['safe_code'][]="exec(\$cmd, \$output";
$config['safe_code'][]='eval($out . '."';');";
$config['safe_code'][]="mf_exec(";
$config['safe_code'][]="eval (/";
$config['safe_code'][]="eval(\"s";
$config['safe_code'][]="eval(\"d";
$config['safe_code'][]="eval(\"\\\$file = \\\"\$file\\\";\");";
$config['safe_code'][]="eval(\$(\"#cate_\" id))";
$config['safe_code'][]="eval(\"\\\$this->";
$config['safe_code'][]="eval(\"\\\$var = \$expr;\");";
$config['safe_code'][]="exec(\$cmd, \$out, \$ret);";
$config['safe_code'][]="shell_exec(\$this->";
$config['safe_code'][]="curl_multi_exec";
$config['safe_code'][]="WP_Filesystem";
$config['safe_code'][]="WP_Filesystem";
$config['safe_code'][]="filesystem";
$config['safe_code'][]="callEval";
$config['safe_code'][]="globalEval";
$config['safe_code'][]="another system";
$config['safe_code'][]="eval(\$(\"#cate_\"";
$config['safe_code'][]="system(\"tar ";

//안전한 파일과 사용 함수명들
$config['safe_file']=array(
	 './lib/Excel/php_writeexcel/class.writeexcel_format.inc.php' => "eval(\"\\\$this->set_\$value();\");"
	,'./lib/Excel/php_writeexcel/class.writeexcel_format.inc.php' => "eval(\"\\\$this->set_\$key(\'\$value\');\");"
	,'./plugin/kcpcert/lib/ct_cli_lib.php' => "\$rt = exec( \$exec_cmd );"
	,'./plugin/social-login/bbs/register_form.php' => "eval(\$skin);"
	,'./plugin/social-login/bbs/register_form_update.php' => "eval(\$file);"
	,'*/bbs/register_form.php' => "eval(\$skin);"
	,'*/bbs/register_form_update.php' => "eval(\$skin);"
);


//패턴 제한수 안전한 파일들
$config['safe_file_limit']=array(
	 '*/lib/Excel/php_writeexcel/class.writeexcel_workbook.inc.php'=>611
	,'*/safe_file_limit.php'=>999
);

if ($config['show']) echo "<xmp>";

$config['ar_count_path']=0;
$config['ar_count_file']=0;
$config['ar_count_hack']=0;
$config['ar_start_time']=time();
ar_check_dir(".");
$config['ar_finish_time']=time();
if ($config['show']) echo "</xmp>";
echo "총 디렉토리수 : ".number_format($config['ar_count_path'])," 개<BR>";
echo "총 검사파일수 : ".number_format($config['ar_count_file'])," 개<BR>";
echo "총 감염의심수 : ".number_format($config['ar_count_hack'])," 개<BR>";
echo "총 소요시간초 : ".number_format($config['ar_finish_time']-$config['ar_start_time'])," 초<BR>";

// 여기부터 메인 소스

function ar_check_dir($path) {
	global $config;
	$config['ar_count_path']++;
	$handle = @opendir($path);
	if (!$handle) return;
	if ($config['show']) echo "path:({$config['ar_count_path']}) $path\n";
	while ($file = readdir($handle)) 
	{
		$filename=$path.'/'.$file;
		if($file == "."||$file == "..") continue;
		if($file == basename(__FILE__)) continue;
		if (is_dir($filename)) ar_check_dir($filename);
		else if (is_file($filename)) ar_check_file($filename);
	}
	closedir($handle);
}

function ar_check_file($filename) {
	global $config;
	$config['ar_count_file']++;
	//if ($config['show']) echo "file:({$config['ar_count_file']}) $filename\n";
	$basename=basename($filename);
	$extend=strtolower(pathinfo($filename, PATHINFO_EXTENSION));

	if ($basename=='.htaccess') ar_check_file_ar_htaccess($filename);
	else if($extend=='php') ar_check_file_ar_php($filename);
	else if(preg_match("/(png|jpg|gif|bmp|jpeg)/i", $extend)) ar_check_file_ar_image($filename);

}

function ar_check_file_ar_htaccess($filename) {
	//echo ".htaccess 파일 ",$filename,"\n";
	$buff=file_get_contents($filename);
	ar_check_safe_url_htaccess($filename,$buff);
}

function ar_check_safe_url_htaccess($filename,$buff) {
	global $config;
	preg_match_all("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/is", $buff,$match);
	//var_dump($match);
	foreach($match[0] as $url) {
		$arr=parse_url($url);
		//echo strtolower($arr['host']),"\n";
		$host=strtolower($arr['host']);
		if (strlen($host)<7) continue;
		if (!in_array($host,$config['safe_url'])) {
			ar_check_hack($filename,"htaccess","승인되지 않은 URL ({$url}) 이 .htaccess 에 있음");
		}
	}


}

function ar_check_file_ar_image($filename) {
	if (!@filesize($filename)) return;
	$timg = @getimagesize($filename);
	if ($timg['2'] < 1 || $timg['2'] > 16) {
		ar_check_hack($filename,'image',' 이미지가 아님');
	}

	//echo "image 파일 ",$filename,"\n";
}

function ar_check_file_ar_php($filename) {
	global $config;
	$buff=file_get_contents($filename);
	ar_check_safe_data_path($filename,$buff);
	ar_check_safe_eval($filename,$buff);
	ar_check_safe_many_times($filename,$buff);
}

function ar_check_safe_data_path($filename,$buff) {
	global $config;
	if (!strstr($filename,'/data/')) return;
	if (preg_match("/(.*)\/data\/file\/(.*)\/(.*).php/i",$filename,$match)) {
		if (!filesize($filename)) return;
		ar_check_hack($filename,'file_index',"데이터 경로에 $match[3].php 가 빈 파일이 아닙니다.");
	}

}



function ar_check_safe_many_times($filename,$buff) {
	global $config;
	$limit=intval(filesize($filename)/500);
	if ($limit<10) $limit=10;
	$limit2=$limit*4;

	foreach($config['safe_file_limit'] as $fi=>$va) {
		$fi2=str_replace('*','',$fi);
		if (strstr($filename,$fi2)) {
			$limit=$limit2=$va;
			break;
		}
	}


	if (($count=substr_count($buff,'$GLOBALS['))>$limit) {
		ar_check_hack($filename,'패턴',"GLOBALS 실행함수 너무 많음 : {$count}개 > $limit");
		//return;
	}
	$count=preg_match_all("/x([a-z0-9]){2}/is", $buff,$match);
	if ($count>$limit2) {
		ar_check_hack($filename,'패턴',"\x 16진수 너무 많음 : {$count}개 > $limit2");
		//return;
	}

}

function ar_check_safe_eval($filename,$buff) {
	global $config;
	$buff=preg_replace("/[a-z0-9_.>]eval/is","",$buff);
	$buff=preg_replace("/[a-z0-9_.>]system/is","",$buff);
	$buff=preg_replace("/[a-z0-9_.>]exec/is","",$buff);
	$buff=preg_replace("/eval\([a-z0-9_.\"]/is","",$buff);
	$buff=str_ireplace($config['safe_code'],"",$buff);
	$arr_tr=array(
		 '$skin'=>'$buff'
		,'$file'=>'$buff'
		,'$temp'=>'$buff'
		,'$value'=>'$buff'
	);
	$buff=strtr($buff,$arr_tr);

	$arr=explode("\n",$buff);
	$line=0;
	foreach($arr as $str) {
		$str=trim($str);
		$line++;
		if (!trim($str)) continue;
		$str=urldecode($str);
		preg_match('/(eval|system|exec)\s*\(/i', $str,$match);
		if ($match) {
			$is_safe=false;
			foreach($config['safe_file'] as $fi=>$va) {
				$va=strtr($va,$arr_tr);
				$fi2=str_replace('*','',$fi);
				if (strstr($filename,$fi2) && strstr($str,$va)) {
					$is_safe=true;
					break;
				}
			}
			if ($is_safe) continue;

			//var_dump($match);
			//$config['ar_count_hack']++;
			//echo " $filename -> ";
			$str2=str_replace('$','\$',addslashes($str));
			//echo $str2,"\n";
			ar_check_hack($filename,'eval',"eval 실행함수 포함 : $line line ".$str2);
		}
	}
}




function ar_check_hack($filename,$type,$msg) {
	global $config;
	$config['ar_count_hack']++;
	echo "## $type ## ({$config['ar_count_hack']}) $filename -> $msg\n";

}
