1. A3 SQL Injection (intro) 풀이
처음 문제는 간단한 것을 물어본다. 위의 Employees Table에서 Bob Franco라는 직원의 부서를 가져오면 된다.
다음과 같이 간단한 sql 문을 작성해 employees 테이블에서 first name이 Bob인 사람의 department를 가져오니 성공했다.
다음 페이지로 넘어가니 DML 관련 설명이 있었다.
DML (Data Manipulation Language)은 데이터 조작 언어로, 데이터베이스 안의 데이터를 조작(추가, 조회, 수정, 삭제) 하는 명령어이다.
대표적인 DML 명령어는 다음과 같다.
SELECT → 데이터 조회 | INSERT → 데이터 추가 | UPDATE → 데이터 수정 | DELETE → 데이터 삭제
이제 문제를 보면 Tobi Barnett의 부서를 Sales로 수정하라고 한다. 데이터를 수정할 때 사용하는 명령어는 update이므로, 이를 사용하면 아래와 같이 department가 Sales로 바뀌면서 성공하게 된다.
다음으로 넘어가게 되면 DDL 관련 설명이 있다.
DDL (Data Definition Language)은 데이터 정의 언어로, 데이터 구조를 정의하는 명령어들을 의미한다.
주로 데이터베이스의 스키마를 정의하는 데 사용된다.
스키마는 데이터베이스의 전체적인 구조/조직으로,
테이블(tables), 인덱스(indexes), 뷰(views), 관계(relationships), 트리거(triggers) 등이 포함된다.
주요 DDL 명령어는 다음과 같다
CREATE → 새로운 데이터베이스 객체 생성(테이블, 뷰, 인덱스 등) | ALTER → 기존 데이터베이스 구조 변경 (컬럼 추가/삭제 등) | DROP → 데이터베이스 객체 삭제
아래와 같이 문제를 보면 위의 스키마(employees)에 phone이라는 이름의(최대 길이 20) 새로운 열을 추가하라고 한다.
그래서 db의 구조를 변경하는 alter 명령어를 이용해 phone 열을 add하고 성공을 했다.
다음은 DCL 관련 페이지이다.
DCL(Data Control Language)은 접근 권한을 제어하는 명령어이다.
누가 어떤 데이터베이스 객체(테이블, 뷰, 함수 등)를 조회/수정/삭제할 수 있는지 권한을 관리하는 역할을 한다.
주요 DCL 명령어로는
GRANT → 권한 부여(특정 사용자에게 권한을 줌) | REVOKE → 권한 회수(이전에 부여한 권한을 회수함) 가 있다.
아래 사진의 문제를 살펴보면 grant_rights 테이블에 대한 권한을 unauthorized_user에게 부여하라고 한다.
아래 사진과 같이 grant 명령어를 이용해 grant_rights에 대한 모든 권한을 unauthorized_user에게 주었더니 성공했다.
다음 페이지로 넘어가면 sql injection에 대한 설명이 있다.
sql injection은 클라이언트 입력값에 악성 SQL 코드를 주입해서 공격하는 기법으로, 검색 필드와 같은 사용자 입력데이터가 올바르게 필터링되지 못한 채 sql 인터프리터로 들어가면 발생할 수 있다.
sql injection의 예시로, 만약 사용자가 폼 필드에 사용자 이름을 입력하면 사용자 정보를 조회할 수 있는 기능이 있다고 하면, 데이터베이스에서 사용자 정보를 조회하는 sql 쿼리는 아래와 같다.
"SELECT * FROM users WHERE name = '" + userName + "'"
username 변수는 클라리언트 입력값을 담으며 쿼리 안에 주입된다.
만약 입력값이 Smith라면 쿼리는 아래와 같다.
"SELECT * FROM users WHERE name = 'Smith'"
이 쿼리는 이름이 Smith인 사용자의 모든 데이터를 조회한다.
하지만!! 만약 공격자가 sql에서 특별한 의미를 가진 문자열(; , -- , ' 등)을 입력하고 이를 검증없이 그냥 들어오게 한다면, 공격자는 원래 의도된 동작을 바꿀 수 있다.
아래의 코드들은 해커가 sql 인젝션 취약점을 노리고 사용자 입력이 가능한 곳에 입력할 수 있는 몇 가지 예시이다.
1. Smith' OR '1' = '1
2. Smith' OR 1 = 1; --
3. Smith'; DROP TABLE users; TRUNCATE audit_log; --
1: 쿼리에 들어가게 되면 'Smith' OR '1'='1'이 되는데 '1'='1'은 항상 true 이고, or은 둘 중 하나라고 true면 전체 조건이 true가 되는 논리 연산자이므로 users 테이블의 모든 행을 반환하게 된다.
-> 한 명의 사용자만 조회하려던 쿼리를 모든 사용자 조회로 바꿔버릴 수 있다.
2: --은 sql에서 주석을 의미하며, 같은 줄의 뒤쪽 코드를 모두 무시한다. 실제로 삽입되는 쿼리에서는 뒤의 작은 따옴표를 무시하게 된다. 1=1은 항상 true이므로 모든 사용자 데이터를 반환하게 된다.
-> 한 명의 사용자만 조회하려던 쿼리를 모든 사용자 조회로 바꿔버릴 수 있다.
3: 세 가지 sql 명령어를 ;로 이어놓았으며 이들이 연속으로 실행되게 된다. 실행이 된다면, 먼저 이름이 Smith인 행을 가져오고, users 테이블을 삭제한 뒤, audit_log 테이블의 모든 행을 삭제(로그 기록까지 지움)하게 된다. 그리고 --로 뒤쪽의 나머지 코드를 무시한다.
-> 데이터베이스 구조를 파괴하며 로그 데이터까지 삭제하게 된다.
다음 페이지로 넘어가게 되면 성공적인 SQL 인젝션으로 할 수 있는 일과 SQL 인젝션 공격이 가능하게 하는 것들에 대한 설명이 나온다. 먼저 할 수 있는 일으로는
- 데이터베이스의 민감한 데이터를 읽거나 수정할 수 있다.
- 데이터베이스에서 관리자급 작업을 실행할 수 있다.
- DBMS 자체를 중지시킬 수 있다.
- 테이블과 로그를 잘라내어 비울(TRUNCATE) 수 있다.
- 사용자 계정을 추가할 수 있다.
- DBMS 파일 시스템에 있는 특정 파일의 내용을 가져올 수 있다.
- 운영체제에 명령을 내릴 수 있다.
정도가 나와있고, 인젝션 공격이 가능하게 하는 것들로는
- 신원을 위조할 수 있다.
- 기존 데이터를 변조할 수 있다.
- 부인 문제를 일으킬 수 있다.
- 시스템의 모든 데이터를 완전히 노출시킬 수 있다.
- 데이터를 파괴하거나 접근 불가능하게 만들 수 있다.
- 데이터베이스 서버의 관리자 권한을 획득할 수 있다.
라고 설명이 나온다. 이 페이지도 설명이 메인인 페이지인 것 같다.
다음 페이지로 넘어가면, SQL 인젝션 공격의 심각성을 제한시키는 요인들에 대해 먼저 나온다. 제한시키는 요인들로는 다계층 방어 대응책, 입력 값 검증, 최소 권한 원칙, 데이터베이스 기술 등이 있다.
또한, 같은 SQL 인젝션 공격이라도 DBMS 종류에 따라 심각도가 달라진다. 아래 예시는 이를 보여준다.
- Microsoft SQL Server → 운영체제 명령 실행, 레지스트리 접근 같은 위험한 확장 기능을 제공.
- MySQL / Oracle / Access → 운영체제 명령 실행을 직접 지원하지 않음.
8페이지도 설명을 하는 페이지인 것 같다. 9페이지로 넘어가보면, 아래와 같은 쿼리 예시를 보여주며 이 쿼리는 문자열을 이어 붙여 만들어지기 때문에 문자열 기반 SQL 인젝션에 취약하다는 설명이 있다.
"SELECT * FROM user_data WHERE first_name = 'John' AND last_name = '" + lastName + "'";
문제를 보면, 유저 테이블에서 모든 유저들을 꺼내오라고 한다. 이를 수행하기 위해서 구체적인 유저들의 이름을 알 필요는 없다는 설명이 함께 있다.
아래와 같이 선택해주면 실제 쿼리가 SELECT * FROM user_data WHERE first_name = 'John' and last_name = 'Smith' or '1' = '1' 다음과 같이 되어서 항상 참이 되므로 모든 유저행을 꺼내오게 되면서 성공을 하게 된다.
10페이지로 넘어가게 되면, 문자열과 같이 아래와 같은 쿼리 예시를 보여주며 이 쿼리는 숫자를 이어 붙여 만들어지기 때문에 숫자형 SQL 인젝션에 취약하다는 설명이 있다.
"SELECT * FROM user_data WHERE login_count = " + Login_Count + " AND userid = " + User_ID;
문제를 보게 된다면, 아래의 두 필드를 이용해서 유저 테이블의 모든 데이터를 가져오라고 한다. 이 때 주의사항이 있는데, 두 필드 중 오직 하나의 필드만 sql 인젝션에 취약하기 때문에 취약한 필드가 어떤 건지 찾아야 한다고 적혀있다.
양쪽에 같은 1 or 1=1 -- 이라는 같은 값을 입력했는데, 숫자가 아닌 값은 입력하지 마라는 글자가 아래에 나왔다. 그래서 두 칸 중 하나는 숫자만 받아들이고 나머지는 필터링 하는 필드고, 이 필드가 아닌 다른 필드가 취약한 필드일 것이라는 생각을 했다.
그래서 Login_count 필드와 User_id 각각에 숫자가 아닌 값인 or 을 넣어보고 어떤 결과가 나오는지 봤다. Login_count 필드에 넣을 때만 숫자만 입력하라는 글자가 나왔다. 이로 인해 취약한 필드는 User_id 필드라는 것을 알게 되었다.
그래서 숫자 제한이 있는 Login_Count 필드에는 간단한 숫자만 넣고, 쿼리가 항상 참이 되도록 해야하므로 취약한 필드에
1 or 1 = 1 -- 라는 값을 입력해 or 뒤의 1 = 1(항상 true) 이후의 값은 -- 로 주석처리를 했다. 이 쿼리는 항상 참이 되므로 모든 유저 정보를 가져오면서 성공을 했다.
다음 11페이지에서는 11,12,13번 페이지에 걸쳐서 SQL 문자열 인젝션이나 쿼리 체이닝과 같은 기법을 사용해 CIA 삼원칙 각각을 훼손하는 방법에 대해 배우게 될 것이라고 한다.
먼저, 기밀성(Confidentiality)을 훼손하는 것에 대해 설명하는데, 성공적인 SQL 인젝션 공격으로 공격자는 데이터베이스에 저장된 신용카드 번호와 같은 민감한 데이터를 읽을 수 있게 되고, 이는 기밀성을 훼손하는 하나의 예시가 될 수 있다.
추가로, 문자열 SQL 인젝션이란, 사용자가 입력한 문자열이 전처리나 준비 없이 그대로 쿼리에 삽입될 경우 입력 필드에 따옴표 등을 넣어 쿼리의 동작을 변경할 수 있는 취약점을 말한다.
문제를 보면, John Smith라는 이름을 가진 사람이 다니는 회사에서는 TAN 이라는 인증 코드를 통해 각자의 부서나 급여 등을 조회할 수 있는 내부 시스템이 있다고 한다. Smith의 TAN은 3SL99A이라고 한다. 하지만 그는 최고 급여를 받고 싶은 생각이 들어, 이 시스템을 이용해 모든 동료들의 데이터를 확인해 급여를 알아보려 하고, 그가 찾은 쿼리는 아래와 같다고 한다.
"SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'";
위와 같이 입력하는 필드가 있다고 할 때 Smith가 모든 직원들의 정보를 알아내기 위해서는 어떻게 입력해야 할까?
문자열 SQL 인젝션을 이용해야 하므로 마지막의 작은 따옴표를 주석처리 해야한다. 첫 번째 필드는 대충 아무 값이나 입력해주고, 두 번째 필드는 1 뒤에 작은 따옴표를 넣어주어 쿼리의 따옴표가 끝나도록 해주며, 뒤에 1 = 1이라는 항상 true인 값을 입력해주고, 뒤의 작은 따옴표를 -- 를 이용해 주석처리 해 주면 쿼리는 항상 참이 되므로 모든 직원의 행을 가져올 수 있게 된다.
다음 12번 페이지로 넘어가면, 쿼리 체이닝으로 무결성(Integrity)을 훼손하는 것에 관한 설명이 나온다.
성공적인 SQL 인젝션 공격은 공격자가 접근 권한이 없어야 하는 정보조차 변경할 수 있게 만들 수 있다. (무결성 훼손)
SQL 쿼리 체이닝이란 말 그대로 쿼리를 이어 붙이는 것을 의미한다. 쿼리 체이닝을 사용하면, 실제 쿼리 끝에 하나 이상의 쿼리를 추가할 수 있고, 이를 위해 ;(세미콜론) 메타문자를 사용한다. ( ;는 SQL 문장의 끝을 표시하며, 새로운 줄을 시작하지 않아도 기존 쿼리 바로 뒤에 다른 쿼리를 실행할 수 있게 해준다. )
문제를 보게 되면 전 문제와 이어서 Smith보다 급여를 더 많이 받는 직원이 두 명있다. 우리는 Smith가 가장 많은 급여를 받도록 해야한다고 하고, 아래 사진과 같이 아까와 같은 두 필드가 있다.
아래와 같이 ; 를 이용해서 select 쿼리 뒤에 update 쿼리를 이어 붙였다. select 쿼리는 대충 name과 TAN 부분에 1을 넣고, 체이닝을 통해 update employees set salary = 90000 where userid = 37648 -- 이라는 쿼리를 이어붙여 userid가 37648인 Smith의 급여를 최고 급여인 90000으로 바꾸고, 뒤의 따옴표는 무시하도록 -- 를 통해 주석 처리를 해 주었다.
마지막 13번 페이지로 가 보면, 가용성(Availability)에 관해서 다루고 있다.
가용성을 위협하는 방법 예시를 아래와 같이 설명한다.
- 계정이 삭제되거나 비밀번호가 변경되면, 실제 소유자는 더 이상 해당 계정에 접근할 수 없게 된다.
- 공격자는 데이터베이스의 일부를 삭제하거나, 심지어 전체 데이터베이스를 삭제(DROP) 하여 데이터를 접근할 수 없게 만들 수도 있다.
- 관리자나 다른 사용자의 접근 권한을 취소(Revoke) 할 수도 있다.
문제를 보면, 급여는 잘 바꿨지만 방금 모든 급여를 확인하고, 급여를 바꾼 모든 쿼리들이 access_log에 남아있다. 우리의 목표는 누군가 알아채기 전에 이 기록들을 지우는 것이다.
주어진 필드는 Action contains 필드 하나 뿐이었고, 이 필드는 로그를 검색하는 기능을 가졌다.
로그 테이블의 행은 ID TIME ACTION 세 개로 이루어져있는데, 여러 개를 입력해보니 Action contains 필드는 action 부분만 검색하는 기능을 제공한다는 것을 알게 되었다. 이를 바탕으로 생각한 쿼리는 아래와 같다.
"SELECT * FROM access_log WHERE ACTION '" + input + "'"
여기서 input은 사용자가 Action contains에 입력하는 값이다.
이 쿼리를 이용해서 입력창에 1';drop table access_log -- 라는 값을 입력해주면, select 실행 후, 체이닝으로 drop 쿼리를 실행시켜 access_log 테이블 자체를 삭제하게 되면서(가용성 훼손) 성공하게 된다.
2. A5 Cross-Site Request Forgeries 풀이
먼저 1번 페이지에서는 CSRF의 개념에 대한 설명이 있다.
CSRF는 웹사이트를 악용하는 공격의 일종으로, 웹사이트가 신뢰하는 사용자로부터 권한 없는 명령을 전송하게 만든다.
XSS와는 다른데, XSS는 사용자가 특정 사이트를 신뢰하는 점을 악용한 방식이고, CSRF는 사이트가 사용자의 브라우저를 신뢰하는 점을 악용한 방식이다.
CSRF는 다음과 같은 특징을 가진다.
- 사용자의 신원을 기반으로 동작하는 사이트를 대상으로 한다.
- 사이트가 해당 신원을 신뢰하는 점을 악용한다.
- 사용자의 브라우저를 속여 HTTP 요청을 전송하게 한다.
- 부작용이 있는 HTTP 요청을 포함한다.
사용자가 특정 행동을 승인하지 않아도, 브라우저에 저장된 쿠키를 통해 HTTP 요청이 자동 전송될 수 있다.
예시로는 피해자의 이메일 주소 변경, 비밀번호 변경, 상품 구매 등이 있다. 여기서 주의해야 할 점은 단순히 데이터를 가져오는 요청은 공격자가 이득을 보지 못한다. 왜냐하면 응답을 받는 사람은 공격자가 아니라 피해자이기 때문이다.
그러므로 CSRF 공격은 목표 서버의 상태를 변경하는 요청을 통해 공격한다.
2번 페이지로 넘어가면, 단순한 CSRF 공격의 예시가 나오는데 아래와 같은 코드를 사용자가 웹사이트에 로그인되어 있는 상태에서 누르면, 단순한 GET 요청만으로 한 계좌에서 다른 계좌로 돈을 이체할 수 있다.
<a href="http://bank.com/transfer?account_number_from=123456789&account_number_to=987654321&amount=100000">View my Pictures!</a>
3번 페이지로 넘어가면, 외부 소스를 통해 제출 폼에 요청을 보내는 문제가 나온다. 화면에 바로 나오는 제출 버튼을 누르면 외부가 아닌 원래 호스트로 들어왔다면서 플래그 값을 알려주지 않는다. 그래서 html로 form을 따로 만들어 외부에서 제출 버튼을 누르면 이동하는 http://127.0.0.1:8080/WebGoat/csrf/basic-get-flag 위치에 요청을 보내도록 해볼 것이다.
요청을 보내기 전에 hidden input 값을 알아야 한다. 서버가 요청을 정상 처리하려면 해당 값이 포함되어야 하기 때문이다. 그래서 서버의 요청을 가로채기 위해 burp suite를 이용해 제출 버튼을 누를 때의 요청을 가로채서 살펴보았다.
사진과 같이 csrf값과 submit값은 사용자가 별도로 입력하지 않아도, 제출 버튼을 누르면 자동으로 서버로 전송되는 hidden 값이다.
이를 반영해 html을 직접 만들면 아래와 같다.
버프스위트에서 필요한 파라미터를 전달하는 요청이 POST로 전달되었으므로 POST로 요청을 보내도록 하였고, 외부에서 요청만 보내면 성공이기 때문에, csrf 와 submit 값은 아무 값이나 넣어주었다. 마지막에는 타입을 submit으로 제출 버튼을 누르면 위의 히든 파라미터들이 전달되도록 버튼을 만들어 주었다.
html 파일을 저장하고, 로컬에서 열면 '제출' 이라는 버튼이 하나 보이고, 이 버튼을 누르니 바로 http://127.0.0.1:8080/WebGoat/csrf/basic-get-flag 로 이동하면서 아래와 같이 flag 값을 알 수 있게 되었다.
다음 4번 페이지로 넘어가면, 새로운 문제가 나온다.
다른 사람을 대신해서 리뷰를 남기는 문제이다. 아래와 같이 리뷰를 남기는 페이지가 시뮬레이션 되어 있다.
중요한 점은, 리뷰 제출을 이 페이지에서 직접 하는 것이 아니라 외부의 다른 곳에서 제출을 시도해야한다는 점이다. 이전 페이지의 csrf 공격과 유사하다.
버프스위트로 다음 웹고트 페이지를 연 후, 아무거나 입력해서 리뷰 제출을 할 때의 요청을 가로채 확인해보면, http://127.0.0.1:8080/WebGoat/csrf/review 로 아래와 같은 파라미터가 포함되어서 POST 요청으로 가는 것을 알 수 있다.
그래서 필요로 하는 파라미터에 아무 값이나 넣어 추가하고, validateReq 파라미터가 요청 검증용 값으로, 이 값을 이용해 정상적인 폼 제출인지 확인을 하기 때문에 버프스위트를 통해 알게된 validateReq 값을 그대로 넣어준 다음, 제출 버튼을 누르면 다음 폼이 전달되도록 html을 만들어서 실행시켰다.
실행 후, 아래와 같이 성공화면이 나오면서 내가 보낸 값이 리뷰에 잘 올라갔는지 확인해 보라는 문구가 같이 보인다.
다시 웹고트 문제 화면으로 돌아가면 아래 사진과 같이 내가 올린 리뷰가 잘 올라가있는 것을 확인할 수 있게 된다.
5번 페이지로 넘어가면, csrf 방지를 자동 지원하는 프레임워크에 대한 설명이 있다.
대부분의 프레임워크는 기본적으로 csrf 방지를 지원한다. 예를 들어 Angular 같은 프레임워크는 자동으로 csrf 방어 기능을 제공하는데, 서버는 세션을 유지하는 쿠키와 별개로 특별한 쿠키를 만들어 클라이언트로 보낸다. 이 쿠키는 세션 쿠키와 다르게 http-only를 쓰지 않아서 자바스크립트가 읽을 수 있고, Angular는 요청을 보낼 때마다 이 쿠키의 토큰을 HTTP의 헤더 (X-XSRF-TOKEN)에 같이 넣어서 보낸다. 서버는 헤더 값이랑 처음에 보낸 쿠키값이 같은지 확인해서 진짜 같은 사이트에서 발생한 것인지 확인하는 방법으로 csrf를 방지한다.
-> 서버는 세션 쿠키, csrf 토큰 두 가지를 서버가 발급했던 값과 일치하는지 확인하는 것이다.
또 다른 방법이 한 가지 더 있는데, 이 방법은 모든 요청에 커스텀 요청 헤더를 추가하는 방식이다. 서버 측에서는 이 헤더의 존재 여부만 확인하면 되고, 이 헤더가 없으면 요청을 거부한다. 일부 프레임워크는 이 구현을 기본으로 제공한다. 하지만 이 방식은 서버와의 모든 상호작용이 자바스크립트로만 수행된다면 동작한다. 그리고, 이 방법도 우회할 수 있다는 것이 발견되어서 완전히 안전하지는 않다.
다음 페이지로 넘어가면, 한 상황을 가정한다. 어떤 API 서버는 오직 JSON 요청만 받고, CORS 설정은 되어 있지 않아서 다른 사이트에서 XHR로 JSON 요청을 직접 보낼 수는 없다. 그래서 다른 사이트에서 JSON API 요청을 못하니까 csrf로부터 안전하다고 생각할 수 있다.
하지만 이것만으로는 완전한 방어가 되지 않는다. 브라우저에는 JSON 데이터를 전송할 수 있는 다른 방법도 있기 때문이다. 예를 들어, navigator.sendBeacon() (페이지가 닫히거나 이동되기 전에 서버로 데이터를 보내야 하는 분석이나 진단 등을 보내는 메서드)과 같은 브라우저 내장 함수는 초기에는 Content-Type을 임의로 JSON으로 설정해도 cross-origin POST 요청을 보낼 수 있었다.
-> Content-Type 제한만으로 CSRF 방어를 믿지 말고, 서버에서 CSRF 보호를 구현해야 한다. (브라우저 Firefox와 Chrome은 이 문제를 고치긴 했다.)
다음 7번 페이지로 넘어가면, 아래 사진과 같이 외부에서 /csrf/feedback/message 주소로 로그인된 세션을 이용해 사용자가 의도하지 않은 내용을 메일과 같은 형식으로 보내는 문제가 나온다.
먼저 burp suite을 이용해 아래에 있는 메일칸에 아무 값이나 넣고 요청 값을 인터셉트해서 보았더니 아래 사진과 같이 나왔다. 사진과 같이 4개의 값은 무조건 요청에 함께 보내야 한다는 것을 기억해야 한다.
이를 반영해서 아래 사진과 같이 코드를 만들 수 있다. Content-Type 제한이 있어 text/plain 으로 보내기 위해 enctype를 사용해 일반 텍스트로 전송하도록 했다. 그리고, 마지막에 "blank" : " 'value=' " 를 넣지 않으면 value 값이 없어서 서버가 받을 때 마지막에 =가 붙어서 나온다. 이를 막기 위해 특별한 의미를 가지지 않는 필드(blank)를 임의로 추가하여 브라우저가 붙이는 마지막 =를 안쪽으로 흡수하도록 했다.
코드를 실행하고 요청을 보낸 다음, 버프스위트에서 잡아보니 아래와 같이 json 형태로 빠지는 값 없이 잘 전달이 되는 것을 확인할 수 있다.
실행시키면 성공화면이 나오면서 flag 값을 알려준다.
8번 페이지로 넘어가게 되면, 로그인 CSRF 공격에 대한 페이지가 나오게 된다.
로그인 csrf 공격에서는 공격자가 정상 사이트에 공격자의 아이디와 비밀번호로 로그인 요청을 위조한다. 이것이 성공하게 되면, 정상 서버는 Set-Cookie 헤더로 응답하여 브라우저에게 세션 쿠키를 저장하라고 지시한다. 그 결과, 사용자가 공격자 계정으로 정상 사이트에 로그인된 상태가 된다. 그리고 이 세션 쿠키는 이후의 모든 요청을 해당 로그인 세션(공격자의 세션)에 묶어서 사용자의 후속 요청들은 모두 공격자 계정에 연결된다.
이러한 공격이 일으킬 수 있는 결과는 예를 들어, 공격자가 google.com에 계정을 만들고 피해자가 악성 웹사이트를 방문하게 되면, 사용자가 공격자 계정으로 로그인된 상태가 된다. 이후 공격자는 사용자의 활동에 관한 다양한 정보(검색 기록 동기화, 알림, 추천, 위치/활동 로그 등)를 수집할 수 있다. 아래 사진은 이 상황을 나타낸 그림이다.
문제를 보면, 지금 문제를 보고 있는 탭은 열어 둔 채, 다른 탭에서 csrf- 라는 접두어를 붙여 새 사용자를 만들어라고 한다. 그리고 이 사용자로 로그인을 한다. 그러면 원래 문제를 풀던 탭은 공격자 계정으로 로그인된 상태가 된다. 그런 다은 원래 탭에서 아래의 버튼을 클릭하면, 세션이 공격자 계정으로 묶여있어 공격자는 내가 이 버튼을 눌렀다는 정보를 알 수 있게 된다. 아래의 오른쪽 사진은 공격자 계정으로 로그인 된 채로 원래 탭에서 버튼을 눌렀을 때의 성공 사진이다.
마지막 9번 페이지로 가면 CSRF 영향에 관해 나온다.
csrf의 영향은 로그인한 사용자가 할 수 있는 권한 범위에만 제한된다. CSRF 공격에 특히 취약한 영역은 IoT 기기와 스마트 가전제품이고, 일반 가정용 라우터들 역시 csrf에 취약하다. 이에 대한 해결책으로는 Same Site 쿠키 속성이 있는데, 최신 브라우저가 지원하는 새로운 확장 기능으로, 쿠키의 적용 범위를 제한하여 same-site 요청일 때만 쿠키가 첨부되도록 한다. 여기에는 strict 모드와 lax 모드 두 가지가 있는데, strict 모드는 cross site 요청을 허용하지 않는다. 예를 들어, github.com에 있을 때 Facebook으로 ‘좋아요’를 누르려 하면 브라우저가 Facebook 쿠키를 첨부하지 않으므로 로그인 페이지로 리다이렉트 된다. 요청의 출발지를 검사해서 요청을 보내는 곳이랑 같으면 쿠키를 첨부하고, 다르면 쿠키를 첨부하지 않는다는 의미이다.
프레임워크에도 csrf 공격을 처리할 수 있는 기능이 기본 내장되어 있는데, 예를 들어 Spring과 Tomcat은 이를 기본적으로 활성화해 둔다. 이 기능을 끄지 않으면 csrf 공격으로부터 안전할 수 있다.
3. A7 Authentication Bypasses 풀이
먼저, 인증 우회에 관한 설명이 나온다. 인증 우회는 여러 방식으로 일어나지만, 보통은 설정이나 논리의 결함을 악용한다. 즉, 조건을 조작해 원하는 상황을 만들어내는 것이다.
가장 단순한 형태로는 웹 페이지/DOM 안에 있는 숨겨진 입력값에 의존하는 경우이다. 공격자는 html 폼의 숨겨진(hidden) input 값을 수정해서 인증을 우회할 수 있는 방법이다.
다른 방법으로는 공격자가 어떤 파라미터의 올바를 값을 알지 못할 경우, 그 파라미터 자체를 아예 제출 요청에서 빼 버리고 결과를 확인하기도 한다.
만약 사이트의 특정 영역이 설정에 의해 적절히 보호되지 않았다면, 그 영역은 경로나 url을 추측하거나 무차별 대입하는 방식으로 접근될 수 있다.
다음 페이지로 넘어가면, 문제가 나온다. 최근에 PayPal의 2단계 인증 비밀번호 재설정 과정에서 발생한 취약점인데, 원래는 사용자가 새 비밀번호를 설정하려면 SMS 코드를 입력해야 했고, 당연히 공격자는 SMS를 못 받는 상황이었다. PayPal은 SMS 대신 보안 질문으로 인증할 수 있는 선택지 또한 제공했기에 공격자는 이를 선택해 버프스위트와 같은 프록시 툴을 사용해 원래 서버에 보내야 하는 중요한 파라미터들을 아예 제거했다. (보통 서버는 method=sms 또는 method=questions 같은 값으로 분기 처리함. 그런데 공격자가 method 같은 파라미터 자체를 지워버림.) 서버는 파라미터가 없을 때 (null) 처리하는 방법을 명확히 하지 않아서 결국 인증을 제대로 거치지 않고 비밀번호 재설정이 성공해 버렸다.
문제는 만약 너가 원래 로그인을 하는 디바이스가 아닌 다른 디바이스에서 로그인을 하려고 하는데 비밀번호가 기억이 나지 않아서 비밀번호를 바꾸려고 한다. 하지만 바꾸기 위해서 질문에 대한 답을 원래 기기에서 설정해놓은 답을 해야했는데 그거도 기억이 나지 않는다. 이러한 상황에서 비밀번호를 바꿔야 하는 것이다.
먼저 두 질문에 대한 답을 각각 임의로 아무 값이나 넣고(test) 보내는 것을 버프스위트로 인터셉트 해서 보면, 아래 사진과 같은 파라미터들이 같이 전달된다. 아래의 사진에서 secQuestion0과 secQuestion1의 값을 없애고 보내면 오류가 생긴다.
하지만 아래와 같이 secQuestion0과 secQuestion1의 번호를 각각 원래 번호가 아닌 다른 이름으로 바꿔서 요청을 보내게 되면, (secQuestion111, secQuestion222) 성공적으로 인증이 우회되어 새 비밀번호를 설정하는 필드가 나오는 것을 볼 수 있다.
'web' 카테고리의 다른 글
웹 기본 ~ CSRF (6) | 2025.07.16 |
---|