레이블이 secure인 게시물을 표시합니다. 모든 게시물 표시
레이블이 secure인 게시물을 표시합니다. 모든 게시물 표시

최근 핵킹 분석 IIS

한동안 쿠키를 이용한 SQL injection이 유행하다가 최근에는 조금 잠잠해 진 듯 하드니 다시 최근에 유행하고 있습니다. 한동안의 공격이 쿠키를 이용한 SQL Injection이 DB의 내용을 변조하는데 주된 행동을 보였다면, 지금의 해킹은 다시 파일을 변조하는 유형을 사용합니다. 파일 변조이니까 다시 과거의 방법으로 막을 수 있지 않을까?라고 생각하실지 모르겠습니다만, 불행하게도 방법이 보다 효과적으로 바뀌었습니다.


일단 해킹에 성공하면 웹쉘을 심는 것 까지는 모두 동일합니다. 하지만, 이후에 파일 변조를 위해 웹사이트를 분석하고 이를 완전 자동화 할 수 있는 방안을 마련합니다. 해킹의 방법까지는 동일하나 그 이후의 과정이 보다 세밀하게 변경되고 보다 찾기는 어렵게 되어가고 있습니다.


과거의 해킹의 내용은 해킹 후 가끔 발견되는 웹쉘이 문제라고 이야기를 했지만, 최근의 해킹은 이 부분에서 보다 진보된 방식을 사용합니다. 조금 극단적인 예를 들면, IIS에서 C:또는 D:와 같은 드라이브를 가상 폴더로 매핑합니다. 여기까지는 윈도우 2000시절의 해킹과 별반 다르지 않습니다. 이런 해킹은 SQL Injection을 통해 파일 접근을 하고 관리자 계정을 탈취하고 백도어 성격으로 만든 것이니까요.



하지만, 이제부터가 문제입니다. 과거의 이런 가상 디렉터리 연결은 파일 브라우징을 쉽게 하기 위해 이뤄진 반면, 최근의 해킹은 자신들이 만든 웹쉘을 찾기 어렵게 만들고자 하는 방안으로 사용됩니다. 과거에 자주 사용했던 방법과 결합한 새로운 방식이지요. 마치 2003년도의 해킹 수법을 보는 듯 합니다.










 2003년도 해킹에 대한 글



 2003년도에 많이 발생했던 해킹들은 눈으로 찾기 어렵도록 꾸미기를 시작합니다. 파일의 위치는 휴지통 폴더에, 그리고 파일명은 매우 유명한 파일명으로, 확장자는 누구도 의심하지 않을꺼 같은 확장자로 꾸미기 작업을 진행하지요.


보통 이런 식으로 많이 쓰이던 확장자는 .jpg나 .gif였습니다만, 이제는 .cer이나 .pfx 같은 인증서 관련 확장자를 사용하기도 합니다. 이 인증서 관련 확장자는 세부 내용 검색에서는 제외되기 때문에 기존의 윈도우에서 제공되는 검색으로는 찾을 수 없습니다.



여기까지는 기존의 방법에서 약간의 변화라고 할 수 있을 것입니다. 하지만, 요즘 해킹의 진정한 문제는 파일 변조를 효과적으로 하기 위하여 현재 웹서버에 있는 파일들을 자동으로 검사하여 변조할 수 있는 ASP 파일이 어딘가 존재한다는 것이지요. 이런 자동화 된 수법은 기존의 모든 패치를 진행했고 웹쉘을 제거했다 하더라도 별도의 실행가능한 파일로 존재하는 것이기에 기존의 웹쉘을 제거하는 것 만으로 끝나지 않습니다.



 대부분, 파일 변조가 일어나면 기존의 백업되어 있는 것으로 덮어씌웁니다만, 백업된 자료에도 이 자동 변조를 위한 asp파일이 심어져 있더군요.


 웹쉘도 제거했고, 분명 부분을 다 봤는데도 불구하고 계속 같은 현상이 발생한다면, 이런 자동으로 동작할 수 있는 소스가 있는지 여부를 한번 확인해보는 것도 좋습니다. 이런 검사는 .asp나 .aspx를 가진 파일 뿐만 아니라 모든 파일을 대상으로 진행되어야 한다는 점이 조금 부담스럽긴 하지만 말이죠.

WEB, HEADER값 조작 그리고 보안

 

 로컬파일로 접근


①번의 방법은 GET방식때문에 막혔습니다 그러면 자바스크립트를 우회하면서 POST방식으로 보내는 방법은 없을까요? 물론 있습니다


웹브라우져는 "소스보기" 라는 좋은 메뉴가 있습니다 있는건 적극 활용합시다 ㅎㅎ


소스보기한 후 바탕화면에 저장하여 약간 손을 보았습니다


즉 입력값을 체크하는 자바스크립트를 무력화 시키면서, action을 수정해 주었습니다


 


C:\Documents and Settings\Administrator\바탕 화면\client.html



<script>


/* 입력한값이 13자리인지 체크 후 전송 */


function validateValue() {


    return true;
}


</script>


 


<form name=f method=post action="http://www.jakartaproject.com/html/process.jsp" onsubmit="return validateValue()">
주민번호 <input type=text name=register_no> <input type=submit name=btn value=submit>
</form>


 


 


웹화면은 다음과 같겠죠




"submit"버튼을 클릭합니다 ^^


 


역시나 자바스크립트를 거치지 않고 process.jsp에 13자리수가 아닌 "22"값을 무난히 보냈습니다




 


그럼 서버쪽에서는 "어이쿠 이런 헤더정보도 체크해야 겠군" 하고 다음 코드를 추가해 버릴겁니다


즉 레퍼러값이 null인 경우는 허용할수 없다는 것이죠


 


http://www.jakartaproject.com/html/process.jsp



<%@ page contentType="text/html; charset=euc-kr" %>


 


<%


if (!"POST".equals(request.getMethod())
   return;



if (request.getHeader("referer") == null)
   return;


String register_no = request.getParameter("register_no");
%>


 


헤더정보 <%=request.getHeader("referer")%><br><br>
주민번호 <%=register_no%>


그럼 위와같은 소스가 될것입니다


그럼 과연 위 소스가 안전할까요?


 


 HEADER값 조작


그렇다면 HEADER값을 조작해 봅시다 어떻게 조작할 수 있을까요?


여러가지 프로그램들이 있지만 그중에 Achilles 라는 proxy server를 이용하여 조작해 보겠습니다


Achilles라는 proxy server는 웹브라우져와 서버간의 HTTP 세션을 중간에서 가로채어 원하는대로 수정한 후 보낼수 있도록 해주는 작으면서도 강력한 프로그램입니다


 



※ 참고


Achilles 문서를 보면 맨 첫줄에 나오는 문장입니다


"Achilles is a tool designed for testing the security of web applications"


이 프로그램은 웹어플리케이션의 보안 테스팅을 하는데 작성되었으므로 테스팅용으로만 사용합시다


 


다운로드


http://www.mavensecurity.com/achilles


 


우선 위에서 작성한 바탕화면에 저장된 C:\Documents and Settings\Administrator\바탕 화면\client.html 를 실행한 결과를 Achilles가 Intercept한 결과입니다

여러 가지 헤더정보들이 보이며 쿠키값까지 조작할수 있습니다


하지만 위의 헤더정보에는 referer값이 없습니다 그래서 referer체크에서 null이 나와 로직에 걸립니다


그렇다면 referer정보를 추가해 줍시다




파랑색으로 줄쳐진 부분을 에디팅하여 추가해주었습니다


Referer: http://www.jakartaproject.com/html/input.html


그런다음 "Send"버튼으로 전송합니다




짠~


그러면 조작된 헤더정보를 알아채지 못하고 헤더의 referer값을 가져오는군요!


 


 


4. 서버쪽 로직 추가!


위에서 자바스크립트를 우회하는 몇가지 방법들을 알아보았습니다


결론은 하나입니다 즉! 서버쪽에서도 동일하게 체크해 주어야 합니다


 



<%@ page contentType="text/html; charset=euc-kr" %>


 


<%


String register_no = request.getParameter("register_no");


 


if (!"POST".equals(request.getMethod())
   return;



if (request.getHeader("referer") == null)
   return;


 


if (register_no.length() != 13)


   return;
%>


 


헤더정보 <%=request.getHeader("referer")%><br><br>
주민번호 <%=register_no%>


 


그렇다면 서버쪽에서만 체크하고 클라이언트에서는 체크할 필요가 없다고 생각할지도 모르지만,


그만큼 서버쪽으로 오는 request를 줄이면 더더욱 좋겠죠


자잘한것 하나때문에 서버쪽 부하를 줄수 없는 노릇아니겠습니까?


 


자 이러이러 하고 저러저러한 이유로 우리는 이제부터라도 클라이언트에서는 자바스크립트로,


서버쪽에서는 또 서버쪽 스크립트로 사용자가 입력한 값에대해 유효성을 검증해야 하는것을 알았습니다


로직이 복잡한 경우 가뜩이나 자바스크립트도 복잡한데 그 로직을 서버쪽에서 다시한번 만든다는게 힘들다는거 다들 압니다 하지만 안전한 웹페이지를 만들기 위해 다같이 노력해야 되지 않겠습니까?


SQL 인젝션 방지

이 포스트를 보낸곳 ()


SQL 인젝션 방지
개발/DB 2006/01/05 15:50
** Sql injection 필요조건

1. 사용자의 입력 값을 필터링, 인코딩 없이 그대로 서버 페이지에서 사용한다.
2. 그 입력 값을 이용해서 서버 페이지는 쿼리를 수행한다.
3. 명령 실행을 위해 사용되는 SQL 서버 계정이 SA일 경우, 피해는 더 커질 수 있다.

--상세한 방법
http://www.taeyo.net/Lecture/NET/Secure_Injection.asp
http://www.unixwiz.net/techtips/sql-injection.html
http://www.securiteam.com/securityreviews/5DP0N1P76E.html
http://www.spidynamics.com/papers/SQLInjectionWhitePaper.pdf


** SQL injection 사전방지
- 사용자의 입력은 결코 신뢰하지 않는다
- 정규 표현식을 사용하여 적법한 입력 외에는 거부한다.
- 저장 프로시저를 사용한다(웹페이지에서 문자열 동적 쿼리는 피한다)
* 최대 문자열의 길이나 타입을 한정한다?
- Sysadmin 계정이 아닌 최소 권한 계정으로 데이터베이스에 연결하게 한다(sa계정을 사용하지 않는다)
- sql 쉘 실행권한을 없앤다. (주기적인 확인필요, 공격시 쉘권한을 복구하여 실행하는경우가 있음)
- 데이터베이스 연결 문자열을 가급적 config 파일에 저장하지 않는다(asp.net의 경우 web.config에 암호화하여 지정)
- 공격자에게 너무 많은 오류 정보를 노출하지 않는다


** SQL injection 방법과 대책

* 웹페이지에서 동적으로 쿼리 빌드 시, 문자열 연결이 아닌 매개변수 쿼리를 사용조작 및 주석처리화 및 에러분석가능

1. 사용자 인증공격 : 정상적인 sql을 변조하여 비정상적으로 통과
** 방식
입력창 : http://duck/index.asp?category=food' or 1=1--
처리되는 쿼리 : SELECT * FROM product WHERE PCategory='food' or 1=1--'

** 로그인경우
rs = select * from user_table where id='ID' and passwd='PWD'
if rs==1
//성공시
else
//실패시.
==> select * from user_table where id='admin' and passwd='' or 'x'='x';
: true, false, true => 결론적으로 true

** 대책
- 사용자의 모든입력값 및 url인자값에 대하여 특수문자 필터링(',<,>,;,--)
- 사용자의 모든 입력값, 크기 및 url인자값에 대하여 sql명령어 금지("/\;:Space--+등과 or, and, union, select, update, insert 등검사)
=> 해당변수의 특수문제 제거및 변형 Request.QueryString["ID"].ToString().Replace("'","\"");

//입력값일 경우 client 자바스크립트로 처리 실제

//한글포함여부
function is_han(val) { //한글이 하나라도 섞여 있으면 true를 반환
var judge = false;
for(var i = 0; i < val.length; i++) {
var chr = val.substr(i,1);
chr = escape(chr);
if (chr.charAt(1) == "u") {
chr = chr.substr(2, (chr.length - 1));
if((chr >= "3131" && chr <= "3163") || (chr >= "AC00" && chr <= "D7A3")) {
judge = true;
break;
}
}
else judge = false;
}
return judge;
}

//한글만 입력가능
function han_only(val) { //한글로만 되있으면 true를 반환 = 영어, 숫자, 특수문자가 있으면 false
var judge = false;
for(var i = 0; i < val.length; i++) {
var chr = val.substr(i,1);
chr = escape(chr);
if (chr.charAt(1) == "u") {
chr = chr.substr(2, (chr.length - 1));
if((chr >= "3131" && chr <= "3163") || (chr >= "AC00" && chr <= "D7A3")) judge = true;
}
else {
judge = false;
break;
}
}
return judge;
}

//영문만 입력확인
function eng_only(val) { //영어로만 되있으면 true를 반환 = 한글, 숫자, 특수문자가 있으면 false
var re = /^[A-Za-z]+$/g;
var rs = re.test(val);
return rs;
}

//이메일주소확인
function is_email(str) { //규정에 맞는 email 주소인지 체크
var r1 = new RegExp("(@.*@)|(\\.\\.)|(@\\.)|(^\\.)");
var r2 = new RegExp("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$");
return (!r1.test(str) && r2.test(str));
}

//숫자만 입력 확인
function is_number(str) {
var r = new RegExp("^[0-9]+$");
return r.test(str);
}

//전화번호 입력 확인
function is_phone(str) {
var r = new RegExp("^[0-9]{2,4}-[0-9]{2,4}-[0-9]{4,4}$");
return r.test(str);
}

//공백제거
function trim(str) { //trim()함수 구현
var newStr = str.replace(/^\s+/,"").replace(/\s+$/,"");
return newStr;
}

//엔터키처리
function enter_key(form) { //enter key를 눌렀을 때 submit
//처럼 사용한다
if (event.keyCode ==13) {
form.submit();
}

}

//날짜형식확인
function is_date(datein){ // 입력날짜의 기본값은 mm/dd/yy이고 다른 형식이면 arguments를 줘야한다.
var type = isDate.arguments[1];
var rval = false;
var indate=datein;
if (indate.indexOf("-")!=-1) var sdate = indate.split("-");
else var sdate = indate.split("/");

if(type=="yy/mm/dd") {
var newdate = Array(3);
newdate[0] = sdate[1];
newdate[1] = sdate[2];
newdate[2] = sdate[0];
indate = newdate.join("/");
sdate = indate.split("/");
}

var chkDate=new Date(Date.parse(indate))
var cmpDate=(chkDate.getMonth()+1)+"/"+(chkDate.getDate())+"/"+(chkDate.getYear())
var indate2=(Math.abs(sdate[0]))+"/"+(Math.abs(sdate[1]))+"/"+(Math.abs(sdate[2]))

if (indate2!=cmpDate) rval = false;
else {
if (cmpDate=="NaN/NaN/NaN") rval = false;
else rval = true;
}
return rval;
}


//주민번호 확인
function is_ssn(SSN1, SSN2) {
if (SSN1.length != 6 || SSN2.length != 7) return false;

var SSN = SSN1 + SSN2;
var strA, strB, strC, strD, strE, strF, strG, strH, strI, strJ, strK, strL, strM, strN, strO;
var nCalA, nCalB, nCalC;

strA = SSN.substr(0, 1);
strB = SSN.substr(1, 1);
strC = SSN.substr(2, 1);
strD = SSN.substr(3, 1);
strE = SSN.substr(4, 1);
strF = SSN.substr(5, 1);
strG = SSN.substr(6, 1);
strH = SSN.substr(7, 1);
strI = SSN.substr(8, 1);
strJ = SSN.substr(9, 1);
strK = SSN.substr(10, 1);
strL = SSN.substr(11, 1);
strM = SSN.substr(12, 1);

// CheckSum
strO = strA*2 + strB*3 + strC*4 + strD*5 + strE*6 + strF*7 + strG*8 + strH*9 + strI*2 + strJ*3 + strK*4 + strL*5;

nCalA = eval(strO);
nCalB = nCalA % 11;
nCalC = 11 - nCalB;
nCalC = nCalC % 10;

strv = '19';
strw = SSN.substr(0, 2);
strx = SSN.substr(2, 2);
stry = SSN.substr(4, 2);

// 날짜수 체크
strz = strv + strw;
if ((strz % 4 == 0) && (strz % 100 != 0) || (strz % 400 == 0)) yunyear = 29;
else yunyear = 28;

if ((strx <= 0) || (strx > 12)) return false;
if ((strx == 1 || strx == 3 || strx == 5 || strx == 7 || strx == 8 || strx == 10 || strx == 12) && (stry > 31 || stry <= 0)) return false;
if ((strx == 4 || strx == 6 || strx == 9 || strx == 11) && (stry > 30 || stry <= 0)) return false;
if (strx == 2 && (stry > yunyear || stry <= 0)) return false;
if (!((strG == 1) || (strG == 2) || (strG == 3) || (strG ==4))) return false;
if ( nCalC != strM ) return false;

return true;
}


//입력시 값 체크 onkeydown="handlerNum()", 최대값 제한 : MaxLength="5"
function handlerNum()
{
e = window.event; //윈도우의 event를 잡는것입니다. 그냥 써주심됩니당.

//숫자열 0 ~ 9 : 48 ~ 57, 키패드 0 ~ 9 : 96 ~ 105 ,8 : backspace, 46 : delete -->키코드값을 구분합니다.
if(e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105 || e.keyCode == 8 || e.keyCode == 46)
{ //delete나 backspace는 입력이 되어야되니까..
if(e.keyCode == 48 || e.keyCode == 96)//0을 눌렀을경우
{
if(txtBox1.value == "" ) //아무것도 없는상태에서 0을 눌렀을경우
e.returnValue=false; //-->입력되지않는다.
else //다른숫자뒤에오는 0은
return; //-->입력시킨다.
}
else //0이 아닌숫자
return; //-->입력시킨다.
}
else //숫자가 아니면 넣을수 없다.
{
alert('숫자만 입력가능합니다');
e.returnValue=false;
}
}


** 동적 쿼리르 사용하지 않고 스토어 프로시저와 파라미터를 사용
private void Get_List(int ID)
{
try
{
string ConnectStr = ConnectionString();

SqlConnection Conn = new SqlConnection(ConnectStr);

SqlDataAdapter da = new SqlDataAdapter();

SqlCommand Cmd = new SqlCommand();

Cmd.Connection = Conn;
Cmd.CommandText = "test_GetJobsID"; //스토어 프로시저사용
Cmd.CommandType = CommandType.StoredProcedure;

Cmd.Parameters.Add("@ID",SqlDbType.Int,4); //파라미터사용
Cmd.Parameters["@ID"].Value = ID; //입력값 체크한후 처리

da.SelectCommand= Cmd;

DataSet ds = new DataSet();

da.Fill(ds,"test");

DataGrid1.DataSource = ds.Tables[0].DefaultView;
DataGrid1.DataBind();
}
catch(SqlException ex)
{
//SQL에러시 처리
}
catch(Exception exp)
{
//에러처리
}
finally
{
//연결종결
}
}

//디비 연결스트링
public string ConnectionString()
{
Base64Code bc = new Base64Code();
return bc.Base64Decode(ConfigurationSettings.AppSettings["ConnectionString"]);
}

// Web.config 설정 추가









- 사용자의 모든 입력값에 대하여 불필요한 에러 메시지 숨김
//Web.config 파일 설정


mode="RemoteOnly" defaultRedirect="/error/errorinfo.aspx">




- 웹어플리케이션이 사용하는 데이타베이스 유저의 권한을 제한


2. MS-SQL상에서 시스템 명령어 실행
** 방식
- xp_cmdshell 을 이용한 시스템 명령실행.
- 기타명령어(xp_startmail, xp_sendmail, xp_dirtree, xp_regdeletekey,
xp_regenumvalues, xp_regread, xp_regwrite, sp_makewebtask, sp_adduser, ...)

** 웹페이지에 악성코드 삽입 방식 실제
; exec master..xp_cmdshell 'ping 10.10.1.2'--
; exec master..xp_cmdshell 'echo >> c:\inetpub\wwwroot\index.html';

** 명령을 삭제하여도 복구후 해킹할수 있으므로 수시확인 필요
num=119' user master dbcc addextendedproc('xp_cmdshell','xplog70.dll')

** 대책
DB의 권한 축소 및 불필요한 sp 제거 : db_owner 권한의 제거 및 일반 user권한 부여

SQL Injection and XSS 진단 스크립트

최근 1~2 년 사이에 해킹 트랜드가, 서버 해킹에서 웹페이지 해킹으로 변하고 있습니다.

다음 내용은, 보안업계에서는 유명하신 바다란 님의 블로그 포스트 입니다.
웹서버를 운영하시는 분은, 꼭 한번쯤은 테스트를 해보시는게 좋지 않나 싶습니다.^^

뭐 직접 웹사이트 개발된것 페이지마다 테스트해보셔도 되지만, 자동화된 툴이 있으면
시간도 절약하고, 좋겠죠?..

레퍼러값 변조후 웹페이지 리턴코드가 500(?) 이 나오거나 예상된 문자가 리턴되면, 무조건 위험성을 내포한 페이지일 가능성이 높습니다.. 웹페이지 갯수는 적더라도, DB에 의해서 링크된 레퍼러값이 많으면 시간 꽤 오래걸립니다.^^

첨부된 압축파일을 풀면, Perl 용 실행파일과 wget.exe 가 있습니다.
Windows 환경에서는 ActivePerl 을 설치해야 합니다. 툴 소스코드가 perl 코드 이기 땜에..

사용법은 gamja.pl http://www.serverinfo.pe.kr/ 형식입니다.
같이 동봉된?.. wget.exe 는 웹페이지 다운로드 유틸이구요.. 리눅스에서 파일다운로드에 많이들 쓰는 유틸입니다. (*제 홈페이지 주소로 테스트 하시면 안되는거 아시죠?^^ 허가되지 않은 테스트는 해킹시도에 해당됩니다.)

리눅스의 경우는 거의 대부분 perl 이 기본설치되어 있으므로, wget.exe 부분을 소스코드에서 수정해 주신다음에 체크 하시면 됩니다. 670 번째 줄이네요.^^

#open (sock, "/usr/bin/wget -U Mozilla -q -O - \"$url\"|");
open (sock, "wget -U Mozilla -q -O - \"$url\"|");

결과파일은, 해당 웹페이지명으로 된 html 파일이 생성됩니다.

Problem - XSS Problem Found
Problem - URL Warning: |JDBC|SQL syntax|ODBC|'SQL|error|SQL 쿼리|ORA-|ERROR
Problem - URL Warning: |JDBC|SQL syntax|ODBC|'SQL|error|SQL 쿼리|ORA-|ERROR

각 결과마다 체크한 페이지가 링크되어 있는데요, 링크를 보면 레퍼러 변수중 테스트용 문자가 들어가 부분이 있을것입니다. 말하자면 그 변수값에 다른 문자가 들어가면 웹페이지에 오류가 발생된다는 얘기이고 이것을 바로, sql injection 이 가능하다는 뜻입니다.

이 툴에 대한 자세한 내용이나 질문은, 바다란님 블로그에서 해주시면 잘?? 답변을 해주시지 않을까 기대해 봅니다.^^

----------------------------------------------------------------
안녕하세요. 바다란입니다.

XSS 진단 및 SQL Injection 진단 스크립트를 공개 합니다.

특별한 것은 아니구요.. 지난해에 급하게 작성하여 사용 했던 스크립트이니 참고 하시면 될 것 같습니다.

국내에서는 아직도 많은 사이트들이 해킹을 당하고 있는 실정입니다.

지금의 해킹은 페이지 변조가 아닌 내부 침입을 통해 정보를 가져 가거나 악성코드를 심는 형태로 나타나고 있는데 진입점을 막지 못하기 때문에 계속 문제가 될 수 밖에 없습니다.

이 스크립트는 완전하지는 않으나 100% 중에서 80% 이상의 문제 발견이 가능하므로 대응에 도움이 될 것입니다. 대기업이나 여력이 있는 기업에서는 상용 스캐너를 통해 문제의 진단 및 수정을 진행 하고 있으나 소규모 사이트들은 그런 여력이 없는 상황입니다.

따라서 지난해 부터 공개를 추진 하였으나 메인 몸이라 힘들었구요.. 1년 이상의 시간이 흘러서 이제는 공개를 해도 되지 않나 하는 생각에 공개 합니다. 허접한 스크립트이고 손을 안 본 상태라 개인 메일 주소만 딸랑 넣어 놨습니다.

실행 시간은 사이트 구조에 따라 오래 걸릴 수 있는데..이럴 경우 1~5Mega 정도의 결과 파일이 생성 되면 Ctrl+c 눌러서 중지 시키고 결과에 나와 있는 문제들을 수정하고 다시 돌리시면 과다한 결과 생성 및 중복 생성 문제가 해결이 됩니다.

이름도 감자 입니다. ㅋㅋㅋ

참고하세여.... 국내의 수많은 웹서버들에 도움이 조금 되었으면 합니다.

출처: http://blog.naver.com/p4ssion/50009547486
----------------------------------------------------------------

현재사이트에서 파일다운로드 : gamja-public-p4ssion.zip