문제 링크
https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-multiple-values-in-single-column
목표 & 사용 도구
목표: administrator 계정의 비밀번호를 알아내서 해당 계정으로 로그인을 하는 것입니다.
사용도구: Burp Suite
문제 풀이 - Lab: SQL injection UNION attack, retrieving multiple values in a single column
- 문제 사이트에 접속하여 문제를 확인합니다. 해당 문제는 상품 카테고리 필터에 SQL Injection 취약점이 있다고 하며 UNION을 사용할 수 있다고 합니다. 또한 쿼리에 대한 응답이 화면에 반환이 되며 DB에는 user 테이블이 있고 해당 테이블에는 username과 password 컬럼이 있다고 합니다. ACCESS THE LAB을 클릭하여 랩에 접속합니다.

- 랩에 접속하면 카테고리를 설정할 수 있는데 아무 카테고리를 클릭해 줍니다. (저는 Gifts로 선택했습니다.)

- Burp Suite를 이용해서 카테고리를 선택했을 때의 요청을 Repeater 기능에 세팅합니다.

- 페이로드를 입력해서 먼저 SQL Injection이 가능한지 확인합니다.
- sql query 문 조건식 참일 때
| 페이로드: Gifts'+and+'1'='1 [설명] 페이로드에서 + 기호는 공백(스페이스)를 의미합니다. |

- sql query 문 조건식 거짓일 때
| 페이로드: Gifts'+and+'1'='2 [설명] sql query 문 조건식이 참, 거짓일 때의 따라 화면에 반환되는 응답이 다르다는 것을 알 수 있고 이는 사용자의 입력 값이 sql query 문에 반영이 되고 있으며 조작할 수 있음을 의미합니다. |

- 문제에서 UNION 구문을 사용할 수 있다고 했으며 UNION 구문을 사용하기 위해서는 먼저 컬럼의 개수를 알아야 합니다. order by와 # (주석 기호)를 이용해서 확인하려고 했지만 에러가 발생하는 것을 확인할 수 있습니다.
| [설명] DB마다 주석 처리할 때 기호가 조금씩 다를 수 있습니다. # 기호의 경우 MySQL, MariaDB에서 사용되는 주석 처리 기호이며 해당 기호를 사용했을 때 에러가 발생했으므로 랩에서 사용하는 DB는 MySQL, MariaDB가 아니라는 것을 알 수 있습니다. 또한 주석을 사용하는 이유는 사용자의 입력 값으로 싱글 쿼터가 들어가면 실제로 완성되는 sql query 문에서 싱글 쿼터가 하나 남아 에러가 발생하기 때문에 해당 에러를 발생시키지 않도록 하려고 주석을 사용하는 것입니다. |

- 이번엔 - 기호를 이용해서 order by와 주석 처리를 시도하여 컬럼의 개수를 알아냅니다
| 페이로드: Gifts'+order+by+1-- (응답 정상 반환) 페이로드: Gifts'+order+by+2-- (응답 정상 반환) 페이로드: Gifts'+order+by+3-- (에러) [설명] - 기호를 이용한 주석은 대부분의 관계형 DB에서 작동하며 order by 1, order by 2 에서 응답이 정상적으로 화면에 반환되고 있으므로 컬럼의 개수가 2개 이상이라는 것을 알 수 있습니다. 이어서 order by 3을 했을 때 응답에서 에러가 발생하게 되는데 세번 째 컬럼은 존재하지는 컬럼을 정렬하려고 했기 때문에 에러가 발생하는 것입니다. 따라서 컬럼은 2개 존재한다는 것을 알 수 있습니다. |
- order by 1

- order by 2

- order by 3

- 컬럼의 개수가 2개인 것을 알아냈으므로 이제 UNION 구문을 사용할 수 있는지 확인합니다.
| 페이로드: '+union+select+null,null-- [설명] union 구문을 사용할 때 대응되는 컬럼의 데이터 타입이 같은 타입이어야 하는 DB들이 있으며 Oracle, PostgreSQL 와 같은 DB들이 해당됩니다. 따라서 union 구문을 사용할 때 null을 사용합니다. null은 특정 타입이 정해진 값이 아니기 때문에 대부분 사용이 가능합니다. (MySQL, MariaDB의 경우 자동으로 데이터 타입이 캐스팅됩니다.) 또한 웹 애플리케이션이 어떤 DB를 사용하느냐에 따라 UNION 구문을 사용하여 공격을 시도할 때 문법이 조금씩 달라지게 됩니다. 예를 들면 Oracle DB의 경우 from 절이 있어야 하기 때문에 from dual을 사용해야 합니다. 이번 페이로드는 Oracle DB가 아니라고 생각하고 페이로드를 사용했습니다. 화면에 에러가 발생하지 않고 응답이 정상적으로 오고 있기 때문에 정상적으로 UNION 구문이 사용 가능하다는 것을 알 수 있습니다. |

- 데이터 타입을 알아내기 위해 첫 번째 컬럼 위치에 숫자를 삽입하고 응답을 확인합니다. 에러가 발생하지 않고 정상적으로 응답이 반환되기 때문에 첫 번째 컬럼은 데이터 타입이 숫자형인 것을 알 수 있습니다.

- 이어서 두 번째 컬럼에도 숫자를 넣고 응답을 확인합니다. 에러가 발생하는 것을 볼 수 있으며 두 번째 컬럼의 경우 데이터 타입이 숫자형이 아니라는 것을 알 수 있습니다.

- 이번엔 두 번째 컬럼에 문자열을 삽입하고 응답을 확인합니다. 에러가 발생하지 않고 응답이 정상적으로 반환되고 있기 때문에 두 번째 컬럼은 데이터 타입이 문자열에 해당한다는 것을 알 수 있습니다. 또한 두 번째 컬럼에 삽입했던 값이 화면에 출력되는 것도 확인할 수 있습니다.

- 두 컬럼의 데이터 타입을 파악했고 어떤 DB를 사용 중인지 알아내기 위해서 DB version을 출력하는 함수를 사용합니다. 이때 DB 별 version을 출력해 주는 함수 또는 구문이 다를 수 있기 때문에 여러 가지를 시도해 봐야 합니다. 화면에 출력되는 컬럼인 두 번째 컬럼에 version() 함수를 입력해 봤을 때 결과를 보면 PostgreSQL을 사용하고 있다는 것을 알 수 있습니다.

- 문제에서 설명했듯이 users 테이블에 username과 password 컬럼이 있기 때문에 다음 페이로드를 삽입하여 administrator 계정이 존재하는지 확인합니다.
| 페이로드: '+union+select+1,username+from+users-- [설명] users 테이블로부터 username 컬럼의 데이터를 가져옵니다. 두 번째 컬럼에 값이 들어갔을 때 화면에 출력이 되기 때문에 두 번째 컬럼에 username 컬럼을 삽입해야 합니다. |

- administrator 계정이 존재하기 때문에 해당 계정의 비밀번호를 알아내는 페이로드를 작성하여 비밀번호를 알아냅니다.
| 페이로드: '+union+select+1,password+from+users+where+username='administrator'-- [설명] users 테이블에서 username이 administrator인 계정의 비밀번호를 가져옵니다. |

추가) 다음과 같은 페이로드로 각 계정과 비밀번호를 한 번에 출력할 수도 있습니다.
| 페이로드: '+union+select+null,username||'-'||password+from+users-- [설명] PostgreSQL에서 || 연산자는 문자열을 연결할 때 사용하는 연산자입니다. 예를 들면 입력: 'te' || ' ' || 'st' 결과: te st 이런 식입니다. 현재 화면에 출력되는 컬럼은 두 번째 컬럼 하나뿐입니다. 따라서 사용자의 계정과 비밀번호를 한 번에 출력할 수 없는 상황이지만 문자열을 연결해 주는 || 연산자를 사용한다면 한 번에 여러 개의 컬럼 데이터를 추출할 수 있게 됩니다. username||'-'||password 를 입력하면 (유저이름)-(비밀번호)가 화면에 출력되게 됩니다. 여기서 - 기호는 사용자의 이름과 비밀번호를 구분하기 위해서 사용한 기호입니다. |

- My account 버튼을 클릭해서 로그인 입력 창으로 이동한 뒤 알아낸 비밀번호를 가지고 로그인 administrator의 계정으로 로그인을 시도합니다.

- UNION SQL Injection 공격을 통해 알아낸 administrator 비밀번호로 로그인이 되면서 해당 랩 문제를 클리어하게 됩니다.

'[WEB] CTF & Wargame > PortSwigger Web Security Academy' 카테고리의 다른 글
| Lab: SSRF with whitelist-based input filter (0) | 2026.06.26 |
|---|---|
| Lab: SSRF with blacklist-based input filter (0) | 2026.06.23 |
| Lab: SSRF with filter bypass via open redirection vulnerability (0) | 2026.06.21 |
| Lab: Web cache poisoning with an unkeyed header (0) | 2026.05.02 |
| Lab: Blind SQL injection with time delays and information retrieval (1) | 2026.04.25 |
| Lab: Blind SQL injection with conditional errors (0) | 2026.03.27 |







































