문제 유형
•
SQLite Injection, Option 문제
문제 정보 확인
Flavortext를 찾아 달라는 문제이다.
index.js 파일의 내용은 아래와 같다.
const crypto = require('crypto');
const db = require('better-sqlite3')('db.sqlite3')
// remake the `users` table
db.exec(`DROP TABLE IF EXISTS users;`);
db.exec(`CREATE TABLE users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
password TEXT
);`);
// add an admin user with a random password
db.exec(`INSERT INTO users (username, password) VALUES (
'admin',
'${crypto.randomBytes(16).toString('hex')}'
)`);
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// parse json and serve static files
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('static'));
// login route
app.post('/login', (req, res) => {
if (!req.body.username || !req.body.password) {
return res.redirect('/');
}
if ([req.body.username, req.body.password].some(v => v.includes('\''))) {
return res.redirect('/');
}
// see if user is in database
const query = `SELECT id FROM users WHERE
username = '${req.body.username}' AND
password = '${req.body.password}'
`;
let id;
try { id = db.prepare(query).get()?.id } catch {
return res.redirect('/');
}
// correct login
if (id) return res.sendFile('flag.html', { root: __dirname });
// incorrect login
return res.redirect('/');
});
app.listen(3000);
JavaScript
복사
index.js 에서 얻을 수 있는 정보
•
const db = require('better-sqlite3')('db.sqlite3')
해당 문제의 DB는 SQLite를 사용한다.
•
if ([req.body.username, req.body.password].some(v => v.includes('\'')))
입력한 req.body.username 과 req.body.password 에서 ' 문자가 존재하는지 탐지
1. OBJECT 형태 값 전달 / 필터링 BYPASS
문제 페이지로 접속하면 로그인을 할 수 있는 페이지를 확인할 수 있다.
그래서 필자는 이 문제가 SQL INJECTION 문제라고 생각하고 많은 삽질을 하였다. 결론적으로 Injection 문제는 맞지만, Injection 구문을 성공시키기 위해서는 한가지 방법을 더 추가해야 했다.
그게 바로 bodyParser.urlencoded({ extended: true }) 를 이용하는 방법이다.
bodyParser.urlencoded({ extended: true }) 를 사용하기 때문에 아래와 같이 object 값을 전달하여도 서버에서 값을 사용할 수 있다.
https://url.kr?test[]=123
Plain Text
복사
?test[]=123 으로 요청할 경우 ["123"] 으로 값이 넘어오는것을 확인할 수 있다.
이렇게 넘어온 오브젝트 형 값은 includes() 함수의 필터링도 걸리지 않는다.
2. SQLite Injection
위 과정을 거쳐 필터링 우회까지 성공한 후, 쿼리문을 작성할 때도 큰 문제없이 쿼리문이 작성된다.
아래 index.js의 쿼리문 작성 부분과 동일하게 값을 설정해 보았고, 오브젝트 형 데이터를 사용해도 쿼리문이 잘 작성되는 것을 확인할 수 있었다. 아마 여러 값이 들어간 오브젝트가 아니라서 그런것 같다.
const query = `SELECT id FROM users WHERE
username = '${req.body.username}' AND
password = '${req.body.password}'
`;
JavaScript
복사
아래처럼 전송하면 위 예시처럼 문제의 SQLite 구문도 동일하게 우회가 가능할 것이다.
?username=admin&password[]='+or+1%3d1--
Plain Text
복사
우회 성공 시, FLAG가 작성된 페이지를 보여준다.
FLAG
dice{sq1i_d03sn7_3v3n_3x1s7_4nym0r3}