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 |