[wargame.kr] tmitter


소스코드 분석

  • 이번 문제에서 주어진 소스코드는 없습니다.
  • 주어진 코드는 tmitter_user 테이블을 만드는 SQL 쿼리문입니다.
  • 이 단서로는 문제를 못 풀겠네요. 서버를 실행해 문제를 보겠습니다.
create table tmitter_user(
    idx int auto_increment primary key,
    id char(32),
    ps char(32)
);

  • 홈페이지에 접속하면 아래와 같은 화면을 볼 수 있습니다.
  • Sign-In, Sign-Up 버튼 2개가 존재하는데요. 하나씩 살펴보겠습니다.

/join.php

  • 먼저 Sign-Up 버튼을 클릭하면 join.php 경로로 접근됩니다.

  • join.php 의 소스를 살펴보면 관리자 계정으로 회원가입 하라는 힌트가 보입니다.

  • 그러나, admin 계정으로 회원가입을 시도하면 이미 존재한다는 알림창 발생합니다.

  • 음.. 일단 넘어가고, 임의의 아이디로 회원가입을 진행해 보았습니다.

/tmitter.php

  • tmitter.php 페이지는 댓글을 작성할 수 있는 기능이 존재했습니다.
  • 임의의 값을 입력하고 Tmeet 버튼을 누르면 Home 하단에 저장되어 출력됩니다.
    • 이 부분을 토대로, 해당 페이지가 DB와 연동되어 있음을 추측할 수 있습니다.

  • 일반적으로 데이터베이스에 값을 저장할 때는 INSERT 문이 사용됩니다.
    • INSERT 문은 아래와 같은 형식으로 사용됩니다.
INSERT INTO {TABLE_NAME} (컬럼명_N-2, 컬럼명_N-1, 컬럼명_N) VALUES IN (값_N-2, 값_N-1, 값_N)
INSERT INTO {TABLE_NAME} (컬럼명_N-2, 컬럼명_N-1, 컬럼명_N) VALUES (값_N-2, 값_N-1, 값_N)

  • 댓글 입력 폼에 test'; 문자를 저장하면 아래 메시지가 저장됐습니다.
    • admin is me!! can you login with my id?
    • (삽입한 “test” 문자열이 저장된 것은 아님)
  • 정확한 구조는 모르겠지만 DB에 값을 저장하는 쿼리문 조작이 가능해 보입니다.
    • 싱글 쿼터 삽입 시에 응답 값이 달라졌으니 쿼리문을 유추해볼 수 있습니다.
    • INSERT INTO 테이블명(컬럼명 1, 컬럼명 2, 컬럼명 N) VALUES(값 1, 값 2, 값 N)

  • test', test')# 문자열을 저장하면 정상적으로 “test” 문자열이 저장됐습니다.
    • 아직까지도 정확한 쿼리 구조는 알 수 없지만, VALUES(…, ‘문자형’, ‘문자형’) 구조로 쿼리가 끝난다는 사실은 유추할 수 있습니다.

  • 테이블의 컬럼 수를 알기 위해서, 임의의 값 삽입을 시도해 보았습니다.
  • 그 결과 INSERT 쿼리문에 사용되는 컬럼 수는 4개임을 확인했습니다.

  • 여기까지 획득한 정보를 종합해보면 다음과 같습니다.
    • (1) INSERT 쿼리문 사용이 가능하다.
    • (2) INSERT 쿼리에 사용되는 컬럼 개수는 4개다.
    • (3) 우리는 계정정보가 저장된 테이블/컬럼 정보를 알고 있다.
      • (tmitter_user / idx, id, ps)
  • 따라서, 계정 정보가 저장된 tmitter_user 테이블에서 관리자(admin) 계정의 패스워드(ps) 정보를 뽑아내 댓글에 출력(저장)시킬 수 있습니다.
    • test','test2'),(0,null,(SELECT ps FROM tmitter_user WHERE id='admin'),null)#

  • 관리자(admin) 계정으로 로그인 하면 플래그를 확인할 수 있습니다.

번외

  • 문제에서 주어진 쿼리문을 살펴보면 id, ps 컬럼의 길이가 32바이트로 제한되어 있었는데요. MySQL CHAR 타입의 경우 고정길이의 값을 저장하는 특징을 가지고 있기 때문에 범위를 벗어난 값이 입력되면 지정된 길이(32바이트)까지만 입력되고 나머지 부분은 제거되어 저장된다고 합니다. (https://blog.naver.com/parkjy76/30051844359)

  • 추가로, 아래와 같이 DB 버전 정보를 확인하였는데요. (MariaDB 5.5.64)
  • MySQL 5.0.3 버전 이후로는 트레일링 스페이스(뒤쪽 공백)가 제거되지 않고 저장된다고 합니다.
    • ‘admin ’ 으로 저장하면 뒤 공백이 그대로 DB에 저장되는 것이죠.

  • 실제로 어떻게 DB에 저장되는지 MySQL 5.6 환경에서 테스트를 해보았습니다.
  • CHAR 타입 컬럼에 ‘admin’, ‘admin ’ 값을 저장한 후에 ‘admin’ 문자열로 데이터를 조회해보면 2개의 행이 모두 조회되고, 결과 출력 시에는 ‘admin ’ 뒤의 공백 또한 제거(trim)되어서 출력됩니다.

  • 위 내용을 바탕으로 내용을 정리해보면 다음과 같습니다.
  • (1) 문제에서 이미 생성되어 있던 admin 계정은 tmitter_user 테이블의 0번째 인덱스(idx)에 저장되어 있었다.
  • (2) Sign-Up 버튼을 통해 신규 생성된 계정은 auto_increment 설정에 의해 순차적으로 인덱스(idx) 값이 할당된다.
  • (3) MySQL CHAR 타입의 고정변수 특징을 이용하여 회원가입을 진행하면 아래와 같이 데이터가 저장된다.
sql> INSERT INTO tmitter_user(idx, id, ps) VALUES(NULL, 'admin                           ', 'adminadmin');
sql> SELECT * FROM tmitter_user WHERE id='admin';
 
idx          id                    ps
  0       admin          {기존 ADMIN 계정 패스워드}
  1       admin          adminadmin

  • (4) PHP에서 DB 데이터 조회 시 행이 여러 개이면 일반적으로 첫 번째 행(row)의 값이 사용된다.
  • (5) adminadmin 패스워드로 로그인 하면 2개의 계정이 조회되는데, 우선적으로 0번째 인덱스(idx)에 저장된 admin 계정이 사용되므로 기존에 존재하던 admin 계정으로 로그인 된다.

  • 이러한 흐름으로 관리자 계정 로그인이 가능한 것으로 보이네요!
    • (이미지는 없지만, adminadmin 패스워드로 admin 계정 로그인도 가능했습니다.)

🐛.. 🐛.. 🐛..


Reference

  1. https://blog.naver.com/parkjy76/30051844359
  2. https://a-platform.tistory.com/169
  3. https://sqltest.net/