본문 바로가기

웹해킹

SQL Injection

DBMS(데이터베이스)는 관계형, 비관계형 이렇게 두 종류로 나눠진다.

관계형(Relational) DBMS는 행과 열의 집합인 테이블 형식으로 데이터를 저장하고,

비관계형(Non-Relational)은 테이블 형식이 아닌 key-value 형태로 값을 저장한다.

관계형 DBMS에는 MYSQL, MariaDB, PastgreSQL, SQLite 등이 있고

비관계형 DBMS에는 MongoDB, CouchDB, Redis 등이 있다.

 

<Relational DBMS>

SQL은 RDBMS의 데이터를 정의하고 질의, 수정 등을 하기 위한 언어이다.

SQL은 사용 목적, 행위에 따라 다양한 구조가 존재한다.

DDL(Data Definition Language) - 데이터를 정의하기 위한 언어, 데이터베이스의 생성, 수정, 삭제 등

데이터베이스 생성
CREATE DATABASE Dreamhack;

테이블 생성
USE Dreamhack;
# Board 이름의 테이블 생성
CREATE TABLE Board(
	idx INT AUTO_INCREMENT,
	boardTitle VARCHAR(100) NOT NULL,
	boardContent VARCHAR(2000) NOT NULL,
	PRIMARY KEY(idx)
);

 

DML( Data Manipulation Language) - 데이터를 조작하기 위한 언어, 존재하는 데이터에 대한 조회, 저장, 수정, 삭제 등

데이터 생성
INSERT INTO 
  Board(boardTitle, boardContent, createdDate) 
Values(
  'Hello', 
  'World !',
  Now()
);

데이터 조회
SELECT 
  boardTitle, boardContent
FROM
  Board
Where
  idx=1;
 
 데이터 변경
 UPDATE Board SET boardContent='DreamHack!' 
  Where idx=1;

 

DCL( Data Control Language) - 데이터베이스의 접근 권한 등의 설정

데이터베이스 내에 이용자의 권한 부여: GRANT

권한 박탈: REVOKE

 

<SQL  Injection>

이용자가 SQL 구문에 임의 문자열을 삽입하는 행위를 SQL Injection 이라고 한다.

Select uid from user_table where uid='' and upw=''

이런 SQL 구문에서 uid에 admin을 반환하려면 admin의 pw를 알아야 하지만

uid에 admin' or '1을 쓰면 뒤에 있는 and를 만족시키 않아도 admin을 반환할 수 있다.

 

Blind SQL Injection은 질의 결과를 이용자가 직접 확인하지 못할 때 참, 거짓 결과를 이용해 데이터를 획득하는 기법이다.

예를 들어서 비밀번호의 첫번째 글자가 a인가? 에 대한 답변이 참인지 거짓인지 알 수 있으면 모든 문자에 대해서 물어보면서 비밀번호를 한자리씩 알아낼 수 있다.

ascii와 substr 함수를 이용할 수 있다. substr(str, a, b)는 str의 a번째부터 b번째까지의 문자열을 가져온다.

request.get 함수로 응답 결과를 확인할 수 있다.

 

<Non-Relational DBMS>

관계형 DBMS는 데이터를 2차원 테이블 형태로 저장한다. 

데이터가 많아지면 용량의 한계에 다다를 수 있다는 단점이 있어서 이를 해결하기 위한 것이

비관계형 데이터베이스, NRDBMS, NoSQL이다.

NoSQL은 Redis, Dynamo, CouchDB, MongoDB 등 다양한 DBMS 각각의 구조와 사용 문법을 익혀야 한다.

 

1. MongoDB

MongoDB는 JSON 형태인 Document를 저장한다.

$ mongosh
> db.user.insertOne({uid: 'admin', upw: 'secretpassword'})
{ acknowledged: true, insertedId: ObjectId("5e71d395b050a2511caa827d")}
> db.user.find({uid: 'admin'})
[{ "_id" : ObjectId("5e71d395b050a2511caa827d"), "uid" : "admin", "upw" : "secretpassword" }]

MongoDB에서 데이터를 삽입하고, 조회하는 쿼리의 예시이다.

_id는 primary key의 역할을 한다.

db.inventory.find(
  { $and: [
    { status: "A" },
    { qty: { $lt: 30 } }
  ]}
)

이런 식으로 status 값이 A이고 qty의 값이 30보다 작은 데이터를 조회할 수 있다.

연산자

$eq-equal

$in-in

$ne-not equal

$nin-not in

$and-쿼리 모두 만족

$not-쿼리 식 반전

$nor-쿼리 모두 불만족

$or-쿼리 중 하나 이상 만족

$exists-지정된 필드 존재

$type-지정된 필드가 지정된 유형

$expr-집계 식

$regex-지정된 정규식과 일치

$text-지정된 텍스트를 검

 

2. Redis

Redis는 메모리 기반의 DBMS이다.

읽고 쓰는 작업을 빠르게 수행 가능해서 임시 데이터를 캐싱하는 용도로 자주 사용된다.

 

3. CouchDB

CouchDB는 JSON 형태로 저장한다. 웹 기반의 DBMS로 REST API 형식으로 요청을 처리한다.

POST - 새로운 레코드 추가

GET - 레코드 조회

PUT - 레코드 업데이트

DELETE - 레코드 삭제

_문자로 시작하는 URL, 필드는 특수 구성 요소이다.

/ - 인스턴스에 대한 메타 정보 반환

/_all_dbs - 인스턴스의 데이터베이스 목록 반환

/_utils - 관리자페이지로 이동

/db - 지정된 데이터베이스에 대한 정보 반환

/{db}/_all_docs - 지정된 데이터베이스에 포함된 모든 document 반환

/{db}/_find - 지정된 데이터베이스에서 JSON 쿼리에 해당하는 모든 document 반

 

<NoSQL Injection>

const express = require('express');
const app = express();
const mongoose = require('mongoose');
const db = mongoose.connection;
mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
app.get('/query', function(req,res) {
    db.collection('user').find({
        'uid': req.query.uid,
        'upw': req.query.upw
    }).toArray(function(err, result) {
        if (err) throw err;
        res.send(result);
  });
});
const server = app.listen(3000, function(){
    console.log('app.listen');
});

이용자가 입력한 uid와 upw에 해당하는 데이터를 찾고 출력하는 예제이다.

입력값에 대한 검증을 하지 않기 때문에 오브젝트 타입의 값을 입력할 수 있다.

$ne 연산자 (not equal)을 이용해 uid와 upw가 일치하지 않는 값을 넣고도 조회를 할 수 있다.

 

const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded( {extended : false } ));
const mongoose = require('mongoose');
const db = mongoose.connection;
mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
app.post('/query', function(req,res) {
    db.collection('user').find({
        'uid': req.body.uid,
        'upw': req.body.upw
    }).toArray(function(err, result) {
        if (err) throw err;
        res.send(result);
  });
});
const server = app.listen(80, function(){
    console.log('app.listen');
});

여기서 uid에 admin, upw에 {"$ne":""를 넣으면 

admin 계정의 비밀번호를 얻을 수 있다.

 

Blind NoSQL Injection

Blind SQL Injection과 같은 원리이다.

MongoDB에서는 $regex, $where 등을 이용해서 할 수 있다.

 

const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded( {extended : false } ));
const mongoose = require('mongoose');
const db = mongoose.connection;
mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
app.get('/query', function(req,res) {
    db.collection('user').findOne({
        'uid': req.body.uid,
        'upw': req.body.upw
    }, function(err, result){
        if (err) throw err;
        console.log(result);
        if(result){
            res.send(result['uid']);
        }else{
            res.send('undefined');
        }
    })
});
const server = app.listen(80, function(){
    console.log('app.listen');
});

이 모듈에서 먼저 패스워드의 길이를 알아내기 위해 uid에 admin, upw에 {"$regex":".{5}"}를 넣으면 result가 나오는 것을 확인해서 5자리라는 것을 알 수 있다.

upw에{"$regex":"^a"} 이런식으로 한글자씩 비교하면서 admin이 반환되면 다음 글자로 넘어가는 식으로 해보면

비밀번호 apple을 찾아낼 수 있다.

 

 

'웹해킹' 카테고리의 다른 글

드림핵 Mango 롸업  (0) 2023.11.04
드림핵 simple_sqli 롸업  (0) 2023.11.04
드림핵 csrf-2 롸업  (0) 2023.11.04
드림핵 csrf-1 롸업  (0) 2023.11.03
ClientSide: CSRF  (0) 2023.11.03