CVE-2022-28347: Potential SQLi via QuerySet.explain() on PostgreSQL
CVE-2022-28347, Potential SQL injection in Django QuerySet explain()
Analysis
> https://github.com/advisories/GHSA-w24h-v9qh-8gxj
이 취약점은 PostgreSQL 환경에서 Django QuerySet
의 explain 기능을 수행할 때 발생 가능한 SQL Injection 취약점이다. QuerySet 오브젝트의 explain()
메소드를 수행하면 EXPLAIN 명령어를 사용할 수 있다. 이 명령어를 사용할 때 MySQL과 PostgreSQL에서는 특별히 EXPLAIN 명령어에 옵션을 지정할 수 있다. 이 기능을 구현한 explain()
메소드에서 발생했던 SQL Injection 보안 취약점(CVE-2022-28347)을 취약점을 분석해보고자 한다.
- QuerySet
QuerySet
오브젝트의explain()
메소드 수행하면self.query.explain()
메소드를 수행한다. 이self.query attribute
는django.db.models.sql.query.Query
클래스로, 결국djagno.db.models.sql.query.Query.explain()
메소드를 수행한다. - Query
Query
클래스의explain()
메소드는get_compiler()
메소드를 통해 compiler를 가져오고 사용하는 DB에 맞추어django.db.models.sql.compiler.SQLCompiler
를 상속한 클래스의explain_query()
메소드를 실행시켜준다. - SQLCompiler SQLCompiler 클래스 내부의 explain_query() 메소드에서는 동일 클래스의 execute_sql()를 실행한다. 실제로 SQL Query구문을 실행하는 메소드이며, 이 메소드 안에서는 explain_info를 검사하는데 이것은 2번 과정에서 Query 클래스의 explain() 메소드에서 namedtuple()로 설정된 값이다.
- SQLCompiler execute_sql() 메소드에서는 Query 오브젝트에 설정된 explain_info attribute가 있다면, 동일 클래스의 explain_query_prefix()를 실행한다.
- BaseDatabaseOperatios의 explain_query_prefix() 메소드는 크게 하는 역할이 없다. 인자로 전달된 format, options의 값을 검사하는데 format의 경우 기본적으로 설정되어 있지 않아 format 값을 지정할 수 없고, options는 그냥 값이 존재한다면 ValueError를 raise한다.
- postgresql의 DatabaseOptions는 explain_query_prefix() 메소드에 유의미한 코드가 있다. (아래 참고) 우선 format 값이 지정된 format인지 확인하고, options를 설정한다. 이때 options로 넘겨진 key 값을 검사하지 않고 넘기기 때문에 options에 SQL 구문을 넣으면 SQL Injection이 가능하다.