보안 공부/소스코드 보안약점 진단

소스코드 보안약점 진단 - LDAP 삽입

H.J.World 2021. 12. 17. 10:10
728x90
반응형

소스코드 보안약점 진단 // 소프트웨어 보안약점 진단 // SW 보안약점 진단과 같이 다양한 이름으로 불리는 진단 과업 중 하나이다.

SW개발보안은 해킹 등 사이버공격의 원인인 보안약점을 SW개발단계에서 사전에 제거하고 SW 개발 생명주기의 각 단계별로 수행하는 일련의 보안활동을 통하여 안전한 SW를 개발·운영하기 위한 목적으로 적용하는 개발체계이다.

해당 내용은 KISA에서 발간하는 취약점 진단 가이드 항목을 기준으로 작성한다.

 

9. LDAP 삽입

가. 개요

공격자가 외부 입력을 통해서 의도하지 않은 LDAP(Lightweight Directory Access Protocol) 명령어 를 수행할 수 있다. 즉, 웹 응용프로그램이 사용자가 제공한 입력을 올바르게 처리하지 못하면, 공격 자가 LDAP 명령문의 구성을 바꿀 수 있다. 이로 인해 프로세스가 명령을 실행한 컴포넌트와 동일한 권한(Permission)을 가지고 동작하게 된다.

외부입력값을 적절한 처리 없이 LDAP 쿼리문이나 결과의 일부로 사용하는 경우, LDAP 쿼리문이 실행될 때 공격자는 LDAP 쿼리문의 내용을 마음대로 변경할 수 있다.

나. 보안대책

DN(Distinguished Name)과 필터에 사용되는 사용자 입력값에는 특수문자가 포함되지 않도록 특수 문자를 제거한다. 만약 특수문자를 사용해야 하는 경우에는 특수문자( = + < > # ; \ 등 )가 실행명령 이 아닌 일반문자로 인식되도록 처리한다.

다. 코드예제

serPassword 변수의 값으로 *을 전달할 경우 필터 문자열은 “(&(sn=S*)(userPassword=*))”가 되어 항상 참이 되며 이는 의도하지 않은 동작을 유발시킬 수 있다.

- 안전하지 않은 코드의 예 JAVA -

1: private void searchRecord(String userSN, String userPassword) throws 
NamingException {
2: Hashtable<String, String> env = new Hashtable<String, String>();
3: env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
4: try {
5: DirContext dctx = new InitialDirContext(env);
6: SearchControls sc = new SearchControls();
7: String[] attributeFilter = { "cn", "mail" };
8: sc.setReturningAttributes(attributeFilter);
9: sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
10: String base = "dc=example,dc=com";
//userSN과 userPassword 값에 LDAP필터를 조작할 수 있는 공격 문자열에 대한 검증이 없어
안전하지 않다.
11: String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))";
12: NamingEnumeration<?> results = dctx.search(base, filter, sc);
13: while (results.hasMore()) {
14: SearchResult sr = (SearchResult) results.next();
15: Attributes attrs = sr.getAttributes();
16: Attribute attr = attrs.get("cn");
17: ......
18: }
19: dctx.close();
20: } catch (NamingException e) { … }
21: }

 

- 안전한 코드의 예 JAVA -

1: private void searchRecord(String userSN, String userPassword) throws NamingException {
2: Hashtable<String, String> env = new Hashtable<String, String>();
3: env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
4: try {
5: DirContext dctx = new InitialDirContext(env);
6: SearchControls sc = new SearchControls();
7: String[] attributeFilter = {"cn", "mail" };
8: sc.setReturningAttributes(attributeFilter);
9: sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
10: String base = "dc=example,dc=com";
// userSN과 userPassword 값에서 LDAP 필터를 조작할 수 있는 문자열을 제거하고 사용
11: if (!userSN.matches("[\\w\\s]*") || !userPassword.matches("[\\w]*")) {
12: throw new IllegalArgumentException("Invalid input");
13: }
14: String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))";
15: NamingEnumeration<?> results = dctx.search(base, filter, sc);
16: while (results.hasMore()) {
17: SearchResult sr = (SearchResult) results.next();
18: Attributes attrs = sr.getAttributes();
19: Attribute attr = attrs.get("cn");
20: ......
21: }
22: dctx.close();
23: } catch (NamingException e) { … }
24: }

 

라. 진단방법

LDAP 조회 쿼리가 실행됨을 확인하고(①), LDAP 조회문의 필터에 사용되는 변수가 외부 입력값 인지 확인한 후(②), 해당 변수에 대한 필터링 모듈이 존재하는지 확인한다(③). 필터링 모듈이 존재하 거나 관련 프레임워크에서 적절히 조치할 경우엔 안전한 것으로 판정한다.

1: …DirContext ctx = new InitialDirContext(env);
2: String managerName = request.getParameter(“managerName”);---------------③
3: //retrieve all of the employees who report to a manager
4: String filter = “(manager=” + managerName + “)”;-------------------②
5: NamingEnumeration employees = 
ctx.search(“ou=People,dc=example,dc=com”, filter); --①
6: …

 

- 정탐코드의 예 -

다음의 예제에서는 외부의 입력(name)이 검색을 위한 필터 문자열의 생성에 사용되고 있다. name 변수의 값으로 “*”을 전달할 경우, 필터 문자열은 “(name=*)”가 할당되어 항상 참이 되므로, 인증을 우회하거나 응용프로그램이 의도하지 않은 동작을 하게 되므로 취약한 것으로 판정한다.

1: public void f() {
2: Hashtable env = new Hashtable();
3: env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.ldap.LdapCtxFactory”);
4: env.put(Context.PROVIDER_URL, “ldap://localhost:389/o=rootDir”);
5: try {
6: javax.naming.directory.DirContext ctx = new InitialDirContext(env);
7: // 프로퍼티를 만들고 외부 파일을 로드한다.
8: Properties props = new Properties();
9: String fileName = “ldap.properties”;
10: FileInputStream in = new FileInputStream(fileName);
11: props.load(in);
12; // LDAP Search를 하기 위해 name을 읽는다
13: String name = props.getProperty(“name”);
4: String filter = “(name =” + name + “)”;
15: // LDAP search가 name값에 대한 여과없이 그대로 통과되어 검색이 되어진다.
16: NamingEnumeration answer = ctx.search(“ou=NewHires”, filter, new SearchControls(
));
17: printSearchEnumeration(answer);
18: ctx.close();
19: }
20: catch (NamingException e) { … }
21: …
728x90
반응형