인증과 인가
인증(Authentication) "신분증 보여주세요."
그 사람 본인이 맞는지 확인하는 작업
ex) 비밀번호(지식), 소지수단(핸드폰, otp) 등등
취약해지면? 인증 절차를 무시하게 됩니다.
인가(Authorization) "출입을 허가하겠습니다."
특정 권한을 부여하는 것
ex) 글을 읽는 것, 쓰는 것, 삭제하는 것, 개인 정보를 열람하는 것 등등
취약해지면? 원래는 못해야 하는 걸 할 수 있게 됩니다.
Authentication(인증) 취약점
인증 취약점은 이해하기 쉽기 때문에 간단하게 핵심만 정리하겠습니다.
Case 1 : Cookie를 통해 인증하는 케이스
[문제]
로그인 후 cookie를 어떻게 처리하는지 살펴보니, 서버에서 이 쿠키값만 받고서 인증을 하고 있다.
→ loginUser를 admin으로 바꾸면 관리자로 로그인이 된다!
[정리]
클라이언트 측 정보를 신뢰해서 인증하는 케이스.
마치 자물쇠를 잠그고 열쇠를 옆에 걸어두는 꼴과 같다.
[해결책]
다양한 해결책이 있을 수 있는데, 기본적으로 쿠키에는 사용자 ID나 권한 정보를 직접 저장하면 안됩니다. 사용자가 이를 조작할 수 있기 때문에 서버에서만 검증 가능해야 합니다. 그리고 권한 체크를 클라이언트가 보낸 정보로 하지 말고, 서버에 저장된 정보를 기반으로 확인하도록 해야겠습니다.
Case 2 : Process 점프
[문제]
index.php 화면이 step1.php, 관리자 비밀번호가 필요한 인증 페이지는 step2.php
→ 그 다음은 step3.php겠네? 이동하니 인증 우회 성공했다!
[정리]
어떤 절차가 있는데 중간에 인증 절차를 뛰어넘어버리는 케이스.
[해결책]
서버 측에서 단계별 검증을 수행해야 합니다. 예를 들어, step3.php에서 step2.php를 거쳤는지를 확인하고, 인증되지 않았다면 step1.php로 리디렉션 하도록 합니다.
Case 3 : 파라미터 응답 값 변조
[문제]
로그인 과정을 분석해보니, 로그인 성공시 'result : ok' 반환 후 그 다음에 페이지 이동.
→ admin으로 로그인 후, response를 intercpt하여 result를 ok로 바꾸고 로그인.
[정리]
(프로그래밍 실수로) 서버에서 주는 응답을 가지고 인증을 할 수 있게 해서, 해당 응답을 변조하면 인증이 우회가 되는 케이스. 이는 웹에서는 흔하지 않지만, 모바일 앱에서는 자주 볼 수 있는 취약점입니다.
[해결책]
역시 클라이언트 측의 응답을 기반으로 인증을 수행하지 않고, 서버에서 세션을 저장하고 검증해야 합니다.
Case 4 : 인증 횟수 제한 없는 경우
[문제]
로그인 과정에서 비밀번호 입력 횟수 제한이 없음
→ 무작위 대입 공격으로 admin 계정 비밀번호를 찾았다!
[정리]
인증 횟수를 제한하지 않아 무작위 대입 공격을 허용한 케이스.
[해결책]
일정 횟수(예 : 5회)이상 로그인 실패 시 계정 잠금 또는 일정 시간동안 차단하도록 합니다. 또한 서버 측에서 로그인 시도 횟수를 추적하는 것도 가능합니다. 자동화된 공격을 방어하기 위해 CAPTCHA를 적용하는 것도 좋습니다.
Authorization(인가) 취약점
주요 전략
1. 파라미터 변조
2. 직접 접근
사례별로 정리해볼 것이며, 대다수 해당 사례 안에서 일어나기 때문에 정리해둬야 합니다.
Case 1 : 주석으로 접근 제한한 경우
[목표] 핵미사일 시스템의 발사 버튼 누르기
먼저 일반 계정으로 로그인했습니다.

발사 버튼은 관리자만 이용 가능하다고 하네요.
버프 스위트로 소스 코드를 살펴봅니다.

귀엽게도 버튼이 주석처리 되어있네요...
admin이 아닌 사람에게도 화면에 출력시켜주고 있습니다.
버튼을 클릭하면 fire_attack_danger.php로 이동할 수 있다는 정보까지 모두 노출되어 있습니다.
따라서 바로 해당 php 로 이동해도 되지만, 다음과 같이 response를 변조할 수도 있습니다.
Intercept on → 메인 페이지 이동 → request 항목 우클릭 후 Do intercept → Response to this request

다음 forward를 눌러주면

이제 response를 변조해봅니다.
방금 찾은 주석을 삭제하고, forward합니다.


발사 버튼이 활성화되었습니다.
Case 2 : javascript로 접근 제한한 경우
문제 2-1
목표는 이전 문제와 같습니다.
일반 계정으로 로그인 후, Fire 버튼을 눌러도 alert 알림만 뜨고 있습니다.

권한이 막힌 것 같은데, 버튼을 아무리 눌러도 burp suite에서는 따로 forward하는 요청이 없습니다.
즉, Fire 버튼을 눌러도 서버로 가는 요청이 없다.
auth 과정을 frontend에서 처리하고 있다는 의미가 됩니다.
따라서 index.php 의 response를 한 번 살펴봅니다.

Fire 버튼을 클릭하면, goMenu라는 javascript 함수를 실행하고 있습니다.
index.php에 포함된 user.js에서 그 정체를 확인할 수 있습니다.

따라서 goMenu()로 인가를 받는 과정은 다음과 같습니다.
1. goMenu(code, userlevel) : 두 개의 값을 받음
2. 만약 code 값이 1018 이라면 user_auth_check 실행
3. user_auth_check 에서 userlevel 값이 admin인지를 검증.
4. true라면, php 파일 경로(./fire_nuclear_Attack.php)로 이동시켜줌.
알아낼 수 있는 것
1. admin 계정이라면 "./fire_nuclear_Attack.php" 로 이동하는구나.
2. userLevel이 admin이면 통과가 되는구나.
따라서 알아낸 php 경로로 바로 이동해도 우회 성공이고,
다음 사진과 같이,

index.php 에서 response를 위조하여 userLevel에 admin을 집어넣어도 우회 성공입니다.
또는 console.log에서 goMenu('1018,'admin'); 을 넣어도 되겠습니다.
문제 2-2

이번에도 역시 Fire 버튼을 눌러도 아무런 반응이 없습니다.
index.php에 response 된 내용을 살펴보니

버튼을 클릭하면 goMenu라는 javascript 함수로 '9999', ' ' 라는 두 개의 값을 보내고 있습니다.
그리고 해당 goMenu() 를 index.php에 포함된 user.js라는 파일에서 확인할 수 있었습니다.

javascript 함수로 authorization 하고 있습니다.
다만, php 파일의 경로를 난독화하고 있네요.
스크립트 코드 전문은 아래에 입력해두었습니다.
function user_auth_check(needLevel, userLevel){
if(needLevel == userLevel){
return true;
}else{
return false;
}
}
function goMenu(code, userLevel){
switch (code){
case '9999':
if(user_auth_check('admin',userLevel)){
location.href=(function(){var J=Array.prototype.slice.call(arguments),C=J.shift();return J.reverse().map(function(N,Q){return String.fromCharCode(N-C-14-Q)}).join('')})(20,150,140,136)+(14).toString(36).toLowerCase()+(21).toString(36).toLowerCase().split('').map(function(I){return String.fromCharCode(I.charCodeAt()+(-13))}).join('')+(27610734146).toString(36).toLowerCase()+(function(){var H=Array.prototype.slice.call(arguments),z=H.shift();return H.reverse().map(function(d,J){return String.fromCharCode(d-z-22-J)}).join('')})(32,167,172,168,175,150,168)+(13).toString(36).toLowerCase()+(function(){var h=Array.prototype.slice.call(arguments),c=h.shift();return h.reverse().map(function(D,U){return String.fromCharCode(D-c-24-U)}).join('')})(22,159,92)+(17).toString(36).toLowerCase()+(function(){var f=Array.prototype.slice.call(arguments),M=f.shift();return f.reverse().map(function(n,S){return String.fromCharCode(n-M-50-S)}).join('')})(49,211);
break;
}else{
alert('권한이 없습니다.');
break;
}
case '0417':
location.href=(1111).toString(36).toLowerCase().split('').map(function(r){return String.fromCharCode(r.charCodeAt()+(-71))}).join('')+(21).toString(36).toLowerCase()+(function(){var g=Array.prototype.slice.call(arguments),b=g.shift();return g.reverse().map(function(l,p){return String.fromCharCode(l-b-38-p)}).join('')})(61,212,203,210)+(1109).toString(36).toLowerCase()+(function(){var U=Array.prototype.slice.call(arguments),C=U.shift();return U.reverse().map(function(b,K){return String.fromCharCode(b-C-11-K)}).join('')})(52,176,109)+(637).toString(36).toLowerCase();
break;
default:
alert('없는 메뉴입니다.');
}
}
난독화에 대해서 전혀 모르지만, 궁금했던 나머지 ChatGPT에 넣어서 한번 decoding 해보았는데, 결과가 fire_base_r_worldd.php 라고 하길래.. 반신반의하며 url로 이동해보았습니다. 하지만 역시.. 404가 뜹니다.
아직은 아니지만 언젠가는 난독화에 대해서 공부해볼 날이 온다면 이 코드를 다시 한번 분석해봐야겠습니다.
아무튼 이 부분은 생략하고, goMenu()의 구조를 파악해보면
1. goMenu(code, userlevel) : 두 개의 값을 받음
2. 만약 code 값이 9999라면 user_auth_check 실행
3. user_auth_check 에서 userlevel 값이 admin인지를 검증.
4. true라면, 암호화된 php 파일 경로로 이동시켜줌.
따라서 index.php에서 response를 다음과 같이 변조한다면?

이렇게 userlevel에 admin을 임의로 넣어주면 인가를 우회할 수 있게 됩니다.
Case 3 : 파라미터 변조가 가능함(Guessing 공격)
문제 3-1
[목표] 관리자만 작성할 수 있는 공지사항에 게시글 남기기
일반 계정으로 로그인 후 공지사항 게시판을 확인합니다.

아예 작성 버튼 조차 없습니다. 우선 공지사항을 확인합니다.

당연히 update 버튼도 없습니다.
하지만 url이 수상합니다.
http://ctf.segfaulthub.com:3481/auth5/notice_read.php?id=44&view=1
여기서 id=44, view=1 인 부분이 어쩌면 admin을 구별하는 인자일 수 있습니다.
그리고 notice_read.php가 있다면 notice_write.php가 있을 수 있겠죠. notice_update.php일수도 있고, notice_create.php일수도 있습니다.
또는 id나 view를 바꿔가며 시도해볼만도 하겠습니다.
이렇게 몇 가지 떠오른 것들을 시도해보는 건 그리 힘들지 않으므로 충분히 해볼만한 가치가 있습니다.
그래서 다음과 같이 url을 변조했는데,
http://ctf.segfaulthub.com:3481/auth5/notice_write.php?id=44&view=1
한 번에 성공해서 저도 당황했습니다.

create 버튼까지 달려있는 진짜 작성 페이지입니다.
글을 작성하고, flag를 획득할 수 있었습니다.
문제 3-2
[목표] 관리자만 읽을 수 있는 공지사항 조회하기
로그인 후 공지사항 게시판(notice_list.php)으로 이동합니다.

admn이 작성한 flag 임시 저장 이라는 게시물이 보이네요.
해당 게시물을 클릭하자, 권한이 없다고 alert가 뜨면서 공지사항 게시판으로 redirect됩니다.

burpsuite으로 확인해보면, 서버와 요청을 주고받는 것으로 보아 서버를 통해 인증 과정을 제대로 거치고 있는 것 같습니다. 그리고 그 과정을 intercept 해보아도, 딱히 화면에 띄워지는 코드가 없습니다.
따라서 본래 목적지는 read.php?id=42&view=1 이지만, 권한 체크를 빡세게 하고 있어 열람하기가 힘드네요.
하지만 게시글을 보는 방법은 읽기(read) 만 있는 것이 아니라, 수정(update)도 있습니다.
제가 직접 게시글을 작성해서 올리고, 해당 게시글 수정 버튼을 누른 다음 url 을 확인해보니 다음과 같았습니다.
http://ctf.segfaulthub.com:3481/auth6/notice_update.php?id=86
그렇다면 여기서 파라미터를 조작해볼 수 있겠습니다. 원래 목적지가 read.php?id=42&view=1 이고.. id가 회원 id를 의미한다고 가정하면, 다음과 같이 url 요청을 넣어볼 수 있습니다.
http://ctf.segfaulthub.com:3481/auth6/notice_update.php?id=42

이런, 또 권한이 없다고 뜨네요.
이러면 다 끝난걸까요?
burp suite으로 해당 response를 자세히 살펴봅시다.

앗, 글이 보이네요.
권한체크를 해서 돌려보내기도 전에 이미 response로 내보내고 있었습니다.
이 상태로도 flag 확인이 가능하지만, response를 위조해서 상단의 script 를 통째로 지워버리면, 웹페이지에서도 글 내용을 확인할 수 있습니다.
문제 3-3
[목표] 관리자만 읽을 수 있는 개인정보 조회하기
상당히 싱거운 문제여서 간단한 풀이만 남기겠습니다.
로그인 후 마이페이지로 들어가면, 상단 url이 다음과 같이 되어있습니다.
http://ctf.segfaulthub.com:3481/auth7/mypage.php?user=sfUser
여기서 user 파라미터에 id를 그대로 출력하고 있네요?
여기에 admin을 입력하면 개인정보 조회가 완료됩니다.
http://ctf.segfaulthub.com:3481/auth7/mypage.php?user=admin
참고로 여기서 관리자 id가 admin이라는 것은, 공지사항 게시판에 관리자가 올려놓은 게시물을 보고 알 수 있었습니다.
인가 취약점 해결책
클라이언트 측에서 숨기는건 보안이 아닙니다. 서버 측에서 관리자가 아니라면 중요한 기능(발사 버튼, 관리자 글 작성, 수정, 삭제 등)자체를 렌더링하지 않도록 해야 합니다. 또한 fire_attack_danger.php와 같은 중요한 페이지에 직접 접근해도 관리자 계정이 아니라면 차단할 수 있도록 강제해야 합니다. 세세하게는 다음과 같은 방법들이 추가될 수 있습니다.
1. POST 요청 강제하기
공지사항 작성 등 관리자 전용 기능에서 GET 요청은 차단하고, 반드시 POST 요청으로 접근할 수 있도록 합니다.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
die("🚫 POST 요청만 허용됩니다.");
}
2. 직접 URL 입력 방지
.htaccess 설정으로 일반 사용자는 직접 url을 입력해도 접근이 불가능하도록 합니다.
<Files "notice_write.php">
Require expr %{REMOTE_ADDR} in {"127.0.0.1", "관리자 IP"}
</Files>
3. 로그 모니터링
비인가 접근 시도를 탐지하는 것도 좋겠습니다.
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'admin') {
file_put_contents("unauthorized_access.log", date("Y-m-d H:i:s") . " | Unauthorized access attempt: " . $_SERVER['REMOTE_ADDR'] . "\n", FILE_APPEND);
die("접근 권한이 없습니다.");
}
'Learning Web > 2024~2025 Web Hacking' 카테고리의 다른 글
| [Web Hacking] File Download 취약점 (0) | 2025.02.15 |
|---|---|
| [Web Hacking] File Inclusion 취약점 (0) | 2025.02.15 |
| [Web Hacking] File Upload 취약점 (0) | 2025.02.09 |
| 로그인 페이지에서 SQL injection 방지 : prepared statement (0) | 2025.02.03 |