Study/PHP

간단한 웹쉘 구현

Kostrian 2015. 1. 29. 11:37

1. 사용 언어 : PHP


2. 목적 : SSH 접속이 허용되지 않은 호스팅 서버에서 쉘을 이용해 간단한 작업을 하기 위함


 닷홈에서 무료호스팅 서비스를 받다보니 SSH접속이 제한되어 있다. 간단한 작업을 하려고 해도 파일 삭제, 파일 생성 같은(사실 생성이 아니라 업로드) FTP를 이용한 작업밖에 불가능 하였다. 그래서 생각한것이 PHP에 있는 시스템 함수를 사용하여 간단하게 쉘을 만들어 보면 어떨까 생각이 들었다.


 기본적으로 PHP는 C와 유사하다. 때문에 PHP에도 system함수가 존재한다. 함수의 원형은 다음과 같다.


 

1
string system ( string $command [, int &$return_var ] )

cs


php.net에 있는 예제는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
echo '<pre>';
 

// 'ls'의 마지막 출력 라인을 $last_line에 저장
// 명령 실행 결과 (성공, 실패 등)을 $retval에 저장
$last_line = system('ls'$retval);
 
// Printing additional info
echo '
</pre>
<hr />Last line of the output: ' . $last_line . '
<hr />Return value: ' . $retval;
?>
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
<?php
// mysql등의 연결정보를 저장하고 있는 파일을 참조
require_once($_SERVER['DOCUMENT_ROOT'].'/board/preset.php');
// 페이지의 헤더부분을 로드
include $url['root'].'/header.php';
 
// 특정 멤버가 아니라면 이용할 수 없도록 만듬
if ( $_SESSION['member_idx'] != '1' ) {
exit();
}
 
// 명령을 POST방식으로 넘겼으니 받는것도 POST
$cmd = $_POST['command'];
 
 
// 명령어와 명령 인자를 구분해서 작업하기 위함
$arr = split(' ',$cmd);
 
// 명령 인자에 있는 상대경로를 처리하기 위함
for ( $i = 1$i < count($arr); ++$i ) {
// ./ 같은 상대 경로 처리
if ( strlen($arr[$i]) != 0 && $arr[$i][0] == '.' && $arr[$i][1] != '.') {
$q = "SELECT * FROM shell WHERE idx='1';";
$result = $mysqli->query($q);
$row = $result->fetch_array();
// 모든 .을 DB에 있는 현재 경로로 바꿔준다. 
// 추후 처음에 나오는 .만 처리해주는 방법으로 변경해야 함
$arr[$i] = str_replace('.',$row['pwd'], $arr[$i]);
}
 
// ../같은 상대 경로 처리
if ( strlen($arr[$i]) > 1 && substr($arr[$i], 02) == '..' ) {
$q = "SELECT * FROM shell WHERE idx='1';";
$result = $mysqli->query($q);
$row = $result->fetch_array();
$tmp = explode("/"$row['pwd']);
// 이전 경로로 바꾸기 위해 현재 디렉토리를 삭제
unset($tmp[count($tmp)-1]);
// '/'을 딜리미터로 분해된 배열을 '/'을 사이에 두고 한 문자열로 병합
$row['pwd'] = implode("/"$tmp);
// ..을 DB에 저장된 이전 경로로 바꿔준다.
$arr[$i] = str_replace('..',$row['pwd'], $arr[$i]);
}
}
 
?>
<form method="post" action="./shell.php">
    <!-- 터미널 같은 환경을 위한 TextArea -->
    <textarea rows="20" cols="80">
<?php
 
// 각각의 명령어일때 처리
if ( strtolower($arr[0]) == 'cd' ) {
$cd = $arr[1];
$q = "UPDATE shell SET pwd='$cd' WHERE idx='1'";
$result = $mysqli->query($q);
$sys = system("cd $cd",$ret);
}
// pwd의 경우엔 이 파일이 있는 경로만을 출력해주기 때문에 DB에 있는 경로 출력
else if ( strtolower($arr[0]) == 'pwd' ) {
$q = "SELECT * FROM shell WHERE idx='1';";
$result = $mysqli->query($q);
$row = $result->fetch_array();
$ret = $row['pwd'];
echo $ret;
}
else if ( strtolower($arr[0]) == 'ls' && count($arr) == 1 ) {
$q = "SELECT * FROM shell WHERE idx='1';";
$result = $mysqli->query($q);
$row = $result->fetch_array();
system("ls ".$row['pwd'],$ret);
}
else {
$cmd = implode(" "$arr);
$sys = system($cmd$ret);
}
?>
</textarea>
    <br>
    <input type="text" class="form-control" width="100" name="command">
    <input type="submit" value="exec!" class="btn btn-danger">
</form>
 
<?php
include $url['root'].'/footer.php';
?>
cs


다음 DB에 shell이라는 테이블을 만들고 필드로는 idx(primary key)와 pwd라는 경로를 저장할 필드를 생성하면 된다.


이렇게 까지 만드는 이유는 system함수를 사용하였을때 실제 실행되는 위치는 system함수를 콜하는 파일의 위치에서 일시적으로 실행이 되고 다시 종료가 된다. 즉 cd 명령어를 사용하여도 "한번" 경로를 변경을 하고 쉘을 종료시킨다. 때문에 이러한 경로는 바뀌지 않기 때문에 DB에 절대경로를 넣어주고 상대경로로 입력을 하든 절대경로로 입력을 하든 cd 명령어를 처리하기 위함이다.


'Study > PHP' 카테고리의 다른 글

MySQL에서 PHP로 데이터 읽어오기  (2) 2015.01.29