입력 검증 및 표현 문제는 메타 문자, 대체 인코딩 및 숫자 표현 때문에 발생합니다. 보안 문제는 입력을 신뢰하기 때문에 발생합니다. 문제로는 "Buffer Overflows", "Cross-Site Scripting" 공격, "SQL Injection", 그 외 여러 가지가 있습니다.
super.validate()
를 호출하지 않는 validate()
메서드를 정의합니다.validate()
메서드를 사용하여 폼 속성의 내용을 관련 Validation Form(검증 폼)에 지정된 제약 조건과 대조하여 검사합니다. 즉, 다음 클래스는 검증 프레임워크의 일부인 validate()
메서드를 갖습니다.
ValidatorForm
ValidatorActionForm
DynaValidatorForm
DynaValidatorActionForm
validate()
메서드를 덮어쓰고 사용자 지정 검증 로직을 구현하는 경우, validate()
구현에서 super.validate()
를 호출해야 합니다. 그렇지 않으면 검증 프레임워크가 폼의 내용이 Validation Form(검증 폼)에 맞는지 검사할 수 없습니다. 다시 말해, 주어진 폼의 검증 프레임워크가 비활성화됩니다.
ValidatorForm
ValidatorActionForm
DynaValidatorActionForm
DynaValidaorForm
validate()
메서드를 구현하여 응용 프로그램에 연결되므로 이러한 클래스 중 하나를 확장해야 합니다.
ActionForm
DynaActionForm
ActionForm
을 사용합니다. 이런 경우 일부 필드는 작업 매핑(Action mapping)에서 사용되지 않을 수도 있습니다. 사용하지 않는 필드도 반드시 확인해야 합니다. 가급적이면 사용하지 않는 필드를 제한하여 비워두거나 정의하지 않도록 해야 합니다. 사용하지 않는 필드를 확인하지 않으면 action의 공유 비즈니스 로직으로 인해 공격자가 해당 폼을 다른 용도로 사용하기 위해 수행하는 검증 검사를 무시할 수 있습니다.form-bean
항목을 사용하여 HTML 양식을 작업에 매핑합니다. Struts 구성 파일의 <action-mappings>
요소에<form-bean>
태그를 통해 지정된 관련 작업 양식에 해당하는 항목이 없는 경우 응용 프로그램 논리가 최신 상태가 아닐 수 있습니다.bean2
에 대한 매핑이 없습니다.
<form-beans>
<form-bean name="bean1" type="coreservlets.UserFormBean1" />
<form-bean name="bean2" type="coreservlets.UserFormBean2" />
</form-beans>
<action-mappings>
<action path="/actions/register1" type="coreservlets.RegisterAction1" name="bean1" scope="request" />
</action-mappings>
validate()
메서드를 비활성화합니다.
<action path="/download"
type="com.website.d2.action.DownloadAction"
name="downloadForm"
scope="request"
input=".download"
validate="false">
</action>
ActionForm
클래스를 변경할 때 검증 로직을 업데이트하는 것을 잊기 쉽습니다. 검증 로직이 올바로 유지 관리되고 있지 않음을 입증하는 한 가지 사례가 바로 Action Form(작업 폼)과 Validation Form(검증 폼)의 불일치입니다.
public class DateRangeForm extends ValidatorForm {
String startDate, endDate;
public void setStartDate(String startDate) {
this.startDate = startDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
}
startDate
와 endDate
가 있는 Action Form(작업 폼)을 보여줍니다.
<form name="DateRangeForm">
<field property="startDate" depends="date">
<arg0 key="start.date"/>
</field>
<field property="endDate" depends="date">
<arg0 key="end.date"/>
</field>
<field property="scale" depends="integer">
<arg0 key="range.scale"/>
</field>
</form>
scale
. 세 번째 필드의 존재는 DateRangeForm
을 검증하지 않고 수정했다는 것을 의미합니다.
app.get('/', function(req, res){
let param = req.params['template']
let val = req.params['templateVal']
let template = Handlebars.compile('{{user}}: {{' + param + '}}');
let templateInput = {}
templateInput['user'] = 'John'
templateInput[param] = val
let result = template(templateInput)
//...
});
Example 1
은 Handlebars
를 템플릿 엔진으로 사용하고 사용자 제어 데이터는 컴파일된 템플릿에 연결되어 공격자가 임의의 JavaScript를 실행할 수 있습니다.Echo
라는 클래스를 정의합니다. 클래스는 C를 사용하여 콘솔에 입력된 명령을 사용자에게 돌려보내는 하나의 네이티브 메서드를 선언합니다.
class Echo {
public native void runEcho();
static {
System.loadLibrary("echo");
}
public static void main(String[] args) {
new Echo().runEcho();
}
}
Echo
클래스에서 구현된 네이티브 메서드를 정의합니다.
#include <jni.h>
#include "Echo.h" //javah로 컴파일된Example 1
의 Java 클래스
#include <stdio.h>
JNIEXPORT void JNICALL
Java_Echo_runEcho(JNIEnv *env, jobject obj)
{
char buf[64];
gets(buf);
printf(buf);
}
gets()
를 사용하기 때문에 buffer overflow에 취약합니다. Example 1
의 취약점은 네이티브 메서드 구현의 소스 코드 감사를 통해 쉽게 발견할 수 있습니다. 소스 코드 감사는 C 소스 코드의 가용성 및 프로젝트 구축 방식에 따라 불가능할 수도 있지만 대부분의 경우 이 방법으로 충분합니다. 하지만 Java와 네이티브 메서드 간에 개체를 공유하게 되면 잠재적인 위험이 Java의 부적절한 데이터 처리가 네이티브 코드의 취약점으로 이어지거나 네이티브 코드의 안전하지 못한 작업으로 Java의 데이터 구조가 손상되는 훨씬 심각한 경우로 확대됩니다.Redirect
라는 클래스를 정의합니다. 이 클래스는 JavaScript를 사용하여 문서 위치를 변경하는 하나의 네이티브 JavaScript 메서드를 선언합니다.
import com.google.gwt.user.client.ui.UIObject;
class MyDiv {
...
public static void changeName(final UIObject object, final String name) {
changeName(object.getElement(), url);
}
public static native void changeName(final Element e, final String name) /*-{
$wnd.jQuery(e).html(name);
}-*/;
...
}
Echo
라는 클래스를 정의합니다. 클래스는 C를 사용하여 콘솔에 입력된 명령을 사용자에게 돌려보내는 하나의 네이티브 메서드를 선언합니다.
class Echo
{
[DllImport("mylib.dll")]
internal static extern void RunEcho();
static void main(String[] args)
{
RunEcho();
}
}
Echo
클래스에서 구현된 네이티브 메서드를 정의합니다.
#include <stdio.h>
void __stdcall RunEcho()
{
char* buf = (char*) malloc(64 * sizeof(char));
gets(buf);
printf(buf);
}
gets()
를 사용하기 때문에 buffer overflow에 취약합니다. 또한 buf
가 할당되지만 해제되지 않으므로 memory leak이 발생합니다.Example 1
의 취약점은 네이티브 메서드 구현의 소스 코드 감사를 통해 쉽게 발견할 수 있습니다. 소스 코드 감사는 소스 코드의 가용성 및 프로젝트의 구축 방식에 따라 현실적이지 않거나 불가능할 수도 있지만 대부분의 경우 이 방법으로 충분합니다. 하지만 관리되는 환경과 네이티브 환경 간에 개체를 공유하게 되면 관리되는 코드의 부적절한 데이터 처리가 네이티브 코드의 예기치 않은 취약점으로 이어지거나 네이티브 코드의 안전하지 못한 작업으로 인해 관리되는 코드의 데이터 구조가 손상되는 등 잠재적인 위험이 훨씬 심각한 문제로 확대됩니다.
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var ctl:String = String(params["ctl"]);
var ao:Worker;
if (ctl == "Add) {
ao = new AddCommand();
} else if (ctl == "Modify") {
ao = new ModifyCommand();
} else {
throw new UnknownActionError();
}
ao.doAction(params);
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var ctl:String = String(params["ctl"]);
var ao:Worker;
var cmdClass:Class = getDefinitionByName(ctl + "Command") as Class;
ao = new cmdClass();
ao.doAction(params);
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
인터페이스를 구현하는 개체를 인스턴스화할 수 있습니다. 명령 디스패처가 access control을 계속 담당하는 경우, 프로그래머는 Worker
인터페이스를 구현하는 새 클래스를 만들 때마다 디스패처의 access control 코드를 수정해야 합니다. access control 코드를 수정하지 않으면 일부 Worker
클래스는 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var ctl:String = String(params["ctl"]);
var ao:Worker;
var cmdClass:Class = getDefinitionByName(ctl + "Command") as Class;
ao = new cmdClass();
ao.checkAccessControl(params);
ao.doAction(params);
Continuation
객체의 콜백 메서드를 확인할 수 있도록 허용하면 공격자가 응용 프로그램을 통과하는 예상치 못한 제어 흐름 경로를 생성하여 보안 검사를 우회할 수도 있습니다.continuationMethod
속성을 설정합니다. 이 속성에 따라 응답 수신 시 호출할 메서드의 이름이 결정됩니다.
public Object startRequest() {
Continuation con = new Continuation(40);
Map<String,String> params = ApexPages.currentPage().getParameters();
if (params.containsKey('contMethod')) {
con.continuationMethod = params.get('contMethod');
} else {
con.continuationMethod = 'processResponse';
}
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(LONG_RUNNING_SERVICE_URL);
this.requestLabel = con.addHttpRequest(req);
return con;
}
continuationMethod
속성을 설정할 수 있으므로 공격자가 이름이 일치하는 어떤 함수든 호출할 수 있게 됩니다.
...
Dim ctl As String
Dim ao As New Worker()
ctl = Request.Form("ctl")
If (String.Compare(ctl,"Add") = 0) Then
ao.DoAddCommand(Request)
Else If (String.Compare(ctl,"Modify") = 0) Then
ao.DoModifyCommand(Request)
Else
App.EventLog("No Action Found", 4)
End If
...
...
Dim ctl As String
Dim ao As New Worker()
ctl = Request.Form("ctl")
CallByName(ao, ctl, vbMethod, Request)
...
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
개체에 의해 구현되는 메서드를 호출할 수 있습니다. 명령 디스패처가 access control을 담당하는 경우, 프로그래머는 Worker
클래스 내의 새 메서드를 만들 때마다 디스패처의 access control 로직을 수정해야 합니다. 이 access control 로직이 부실해지면 일부 Worker
메서드가 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
...
Dim ctl As String
Dim ao As New Worker()
ctl = Request.Form("ctl")
If (ao.checkAccessControl(ctl,Request) = True) Then
CallByName(ao, "Do" & ctl & "Command", vbMethod, Request)
End If
...
clazz
에 정의된 함수를 호출할 수 있습니다.
char* ctl = getenv("ctl");
...
jmethodID mid = GetMethodID(clazz, ctl, sig);
status = CallIntMethod(env, clazz, mid, JAVA_ARGS);
...
예제 2: 이전 예제와 유사하게 응용 프로그램은 명령줄 인수에서 호출할 함수의 이름을 검색하기 위해
...
func beforeExampleCallback(scope *Scope){
input := os.Args[1]
if input{
scope.CallMethod(input)
}
}
...
reflect
패키지를 사용합니다.
...
input := os.Args[1]
var worker WokerType
reflect.ValueOf(&worker).MethodByName(input).Call([]reflect.Value{})
...
String ctl = request.getParameter("ctl");
Worker ao = null;
if (ctl.equals("Add")) {
ao = new AddCommand();
} else if (ctl.equals("Modify")) {
ao = new ModifyCommand();
} else {
throw new UnknownActionError();
}
ao.doAction(request);
String ctl = request.getParameter("ctl");
Class cmdClass = Class.forName(ctl + "Command");
Worker ao = (Worker) cmdClass.newInstance();
ao.doAction(request);
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
인터페이스를 구현하는 개체를 인스턴스화할 수 있습니다. 명령 디스패처가 access control을 계속 담당하는 경우, 프로그래머는 Worker
인터페이스를 구현하는 새 클래스를 만들 때마다 디스패처의 access control 코드를 수정해야 합니다. access control 코드를 수정하지 않으면 일부 Worker
클래스는 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
String ctl = request.getParameter("ctl");
Class cmdClass = Class.forName(ctl + "Command");
Worker ao = (Worker) cmdClass.newInstance();
ao.checkAccessControl(request);
ao.doAction(request);
Worker
인터페이스를 구현하는 개체에만 국한되어 있는 것은 아닙니다. 시스템의 모든 개체의 기본 생성자를 호출할 수 있습니다. 개체가 Worker
인터페이스를 구현하지 않는 경우 ClassCastException
이 ao
에 할당되기 전에 발생하지만 생성자가 공격자에게 유리한 작업을 수행하는 경우 이미 피해를 입은 것입니다. 이 시나리오는 간단한 응용 프로그램에서는 비교적 피해 정도가 약하지만 아주 복잡한 큰 응용 프로그램에서는 공격자가 얼마든지 생성자를 찾아 공격 도구로 이용할 수 있습니다.performSelector
메서드의 인수를 제어할 수 있습니다. 이로 인해 공격자가 응용 프로그램을 통해 예기치 않은 제어 흐름 경로를 생성하여 보안 검사를 무시할 수 있습니다.UIApplicationDelegate
클래스에 정의된 메서드 서명과 일치하는 함수를 호출할 수 있습니다.
...
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSString *query = [url query];
NSString *pathExt = [url pathExtension];
[self performSelector:NSSelectorFromString(pathExt) withObject:query];
...
$ctl = $_GET["ctl"];
$ao = null;
if (ctl->equals("Add")) {
$ao = new AddCommand();
} else if ($ctl.equals("Modify")) {
$ao = new ModifyCommand();
} else {
throw new UnknownActionError();
}
$ao->doAction(request);
$ctl = $_GET["ctl"];
$args = $_GET["args"];
$cmdClass = new ReflectionClass(ctl . "Command");
$ao = $cmdClass->newInstance($args);
$ao->doAction(request);
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
인터페이스를 구현하는 개체를 인스턴스화할 수 있습니다. 명령 디스패처가 access control을 계속 담당하는 경우, 프로그래머는 Worker
인터페이스를 구현하는 새 클래스를 만들 때마다 디스패처의 access control 코드를 수정해야 합니다. access control 코드를 수정하지 않으면 일부 Worker
클래스는 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
$ctl = $_GET["ctl"];
$args = $_GET["args"];
$cmdClass = new ReflectionClass(ctl . "Command");
$ao = $cmdClass->newInstance($args);
$ao->checkAccessControl(request);
ao->doAction(request);
Worker
인터페이스를 구현하는 개체에만 국한되어 있는 것은 아닙니다. 시스템의 모든 개체의 기본 생성자를 호출할 수 있습니다. 개체가 Worker
인터페이스를 구현하지 않는 경우 ClassCastException
이 $ao
에 할당되기 전에 발생하지만 생성자가 공격자에게 유리한 작업을 수행하는 경우 이미 피해를 입은 것입니다. 이 시나리오는 간단한 응용 프로그램에서는 비교적 피해 정도가 약하지만 아주 복잡한 큰 응용 프로그램에서는 공격자가 얼마든지 생성자를 찾아 공격 도구로 이용할 수 있습니다.
ctl = req['ctl']
if ctl=='add'
addCommand(req)
elsif ctl=='modify'
modifyCommand(req)
else
raise UnknownCommandError.new
end
ctl = req['ctl']
ctl << "Command"
send(ctl)
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.define_method()
를 사용하여 이러한 메서드를 동적으로 생성하거나 missing_method()
의 오버라이드를 통해 이를 호출하는 것입니다. 이를 감사 및 추적하고 access control 코드를 여기에 사용하는 방법은 매우 까다롭고 어떤 다른 라이브러리 코드가 로드되는지에 따라 달라진다는 점을 고려하면 이 방식은 올바르게 수행하기가 거의 불가능할 수 있습니다.
def exec(ctl: String) = Action { request =>
val cmdClass = Platform.getClassForName(ctl + "Command")
Worker ao = (Worker) cmdClass.newInstance()
ao.doAction(request)
...
}
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
인터페이스를 구현하는 개체를 인스턴스화할 수 있습니다. 명령 디스패처가 access control을 계속 담당하는 경우, 프로그래머는 Worker
인터페이스를 구현하는 새 클래스를 만들 때마다 디스패처의 access control 코드를 수정해야 합니다. access control 코드를 수정하지 않으면 일부 Worker
클래스는 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
def exec(ctl: String) = Action { request =>
val cmdClass = Platform.getClassForName(ctl + "Command")
Worker ao = (Worker) cmdClass.newInstance()
ao.checkAccessControl(request);
ao.doAction(request)
...
}
Worker
인터페이스를 구현하는 개체에만 국한되어 있는 것은 아닙니다. 시스템의 모든 개체의 기본 생성자를 호출할 수 있습니다. 개체가 Worker
인터페이스를 구현하지 않는 경우 ClassCastException
이 ao
에 할당되기 전에 발생하지만 생성자가 공격자에게 유리한 작업을 수행하는 경우 이미 피해를 입은 것입니다. 이 시나리오는 간단한 응용 프로그램에서는 비교적 피해 정도가 약하지만 아주 복잡한 큰 응용 프로그램에서는 공격자가 얼마든지 생성자를 찾아 공격 도구로 이용할 수 있습니다.performSelector
메서드의 인수를 제어할 수 있습니다. 이로 인해 공격자가 응용 프로그램을 통해 예기치 않은 제어 흐름 경로를 생성하여 보안 검사를 무시할 수 있습니다.UIApplicationDelegate
클래스에 정의된 메서드 서명과 일치하는 함수를 호출할 수 있습니다.
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
...
let query = url.query
let pathExt = url.pathExtension
let selector = NSSelectorFromString(pathExt!)
performSelector(selector, withObject:query)
...
}
...
Dim ctl As String
Dim ao As new Worker
ctl = Request.Form("ctl")
If String.Compare(ctl,"Add") = 0 Then
ao.DoAddCommand Request
Else If String.Compare(ctl,"Modify") = 0 Then
ao.DoModifyCommand Request
Else
App.EventLog "No Action Found", 4
End If
...
...
Dim ctl As String
Dim ao As Worker
ctl = Request.Form("ctl")
CallByName ao, ctl, vbMethod, Request
...
if/else
블록 전체가 사라질 뿐 아니라 이제는 명령 디스패처를 수정하지 않고 새 명령 유형을 추가하는 것도 가능합니다.Worker
개체에 의해 구현되는 메서드를 호출할 수 있습니다. 명령 디스패처가 access control을 계속 담당하는 경우, 프로그래머는 Worker
클래스 내의 새 메서드를 만들 때마다 디스패처의 access control 코드를 수정해야 합니다. access control 코드를 수정하지 않으면 일부 Worker
메서드는 access control을 갖지 못합니다.Worker
개체가 액세스 제어 검사 수행을 담당하게 만드는 것입니다. 리팩터링된 코드의 예제는 다음과 같습니다.
...
Dim ctl As String
Dim ao As Worker
ctl = Request.Form("ctl")
If ao.checkAccessControl(ctl,Request) = True Then
CallByName ao, "Do" & ctl & "Command", vbMethod, Request
End If
...
HttpRequest
클래스는 배열 액세스의 형태로 QueryString
, Form
, Cookies
또는 ServerVariables
컬렉션의 변수에 대한 프로그래밍 방식의 액세스를 제공합니다(예: Request["myParam"]
). 동일한 이름을 가진 변수가 두 개 이상 존재할 경우, .NET Framework는 다음 순서로 컬렉션을 검색할 때 먼저 나타나는 변수의 값을 반환합니다. 즉, QueryString
, Form
, Cookies
, ServerVariables
의 순서로 반환합니다. 검색 순서에서 QueryString
이 먼저 오므로 QueryString
매개 변수가 폼, 쿠키 및 서버 변수의 값을 대체할 수 있습니다. 마찬가지로 폼 값은 Cookies
및 ServerVariables
컬렉션의 변수를 대체할 수 있고 Cookies
컬렉션의 변수는 ServerVariables
의 변수를 대체할 수 있습니다.
...
String toAddress = Request["email"]; //Expects cookie value
Double balance = GetBalance(userID);
SendAccountBalance(toAddress, balance);
...
http://www.example.com/GetBalance.aspx
방문 시 Example 1
의 코드가 실행된다고 가정합니다. 공격자가 인증된 사용자로 하여금 http://www.example.com/GetBalance.aspx?email=evil%40evil.com
을 요청하는 링크를 클릭하도록 할 수 있는 경우, 사용자의 잔고 정보가 있는 전자 메일이 evil@evil.com
으로 전송됩니다.HttpRequest
클래스는 배열 액세스의 형태로 QueryString
, Form
, Cookies
또는 ServerVariables
컬렉션의 변수에 대한 프로그래밍 방식의 액세스를 제공합니다(예: Request["myParam"]
). 동일한 이름을 가진 변수가 두 개 이상 존재할 경우, .NET Framework는 다음 순서로 컬렉션을 검색할 때 먼저 나타나는 변수의 값을 반환합니다. 즉, QueryString
, Form
, Cookies
, ServerVariables
의 순서로 반환합니다. 검색 순서에서 QueryString
이 먼저 오므로 QueryString
매개 변수가 폼, 쿠키 및 서버 변수의 값을 대체할 수 있습니다. 마찬가지로 폼 값은 Cookies
및 ServerVariables
컬렉션의 변수를 대체할 수 있고 Cookies
컬렉션의 변수는 ServerVariables
의 변수를 대체할 수 있습니다.www.example.com
으로부터 왔는지 확인합니다.
...
if (Request["HTTP_REFERER"].StartsWith("http://www.example.com"))
ServeContent();
else
Response.Redirect("http://www.example.com/");
...
http://www.example.com/ProtectedImages.aspx
방문 시 Example 1
의 코드가 실행된다고 가정합니다. 공격자가 URL에 대해 직접 요청하는 경우, 적절한 리퍼러 헤더가 전송되지 않으며 요청이 실패합니다. 그러나 공격자가 http://www.example.com/ProtectedImages.aspx?HTTP_REFERER=http%3a%2f%2fwww.example.com
과 같은 필요한 값을 가진 거짓 HTTP_REFERER
매개변수를 전송하는 경우, 조회하면 ServerVariables
대신 QueryString
에서 값이 반환되며 검사가 성공합니다.
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
...
DATA(ixml) = cl_ixml=>create( ).
DATA(stream_factory) = ixml->create_stream_factory( ).
istream = stream_factory->create_istream_string(
`<?xml version="1.0" encoding="UTF-8"?> ` &&
`<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> ` &&
`<stockCheck>&xxe;</stockCheck>` ).
istream->set_dtd_restriction( level = 0 ).
DATA(document) = ixml->create_document( ).
parser = ixml->create_parser(
stream_factory = stream_factory
istream = istream
document = document ).
parser->set_validating( mode = `0` ).
DATA(rc) = parser->parse( ).
...
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/winnt/win.ini" >]><foo>&xxe;</foo>
- (void) parseSomeXML: (NSString *) rawXml {
BOOL success;
NSData *rawXmlConvToData = [rawXml dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *myParser = [[NSXMLParser alloc] initWithData:rawXmlConvToData];
[myParser setShouldResolveExternalEntities:YES];
[myParser setDelegate:self];
}
rawXml
콘텐트를 공격자가 제어할 수 있다고 가정해 보겠습니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
boot.ini
파일의 내용이 포함됩니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>
String xml = "...";
...
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
System.out.println(new String(ch, start, length));
}
};
saxParser.parse(new InputSource(new StringReader(xml)), handler);
} catch (Exception e) {
e.printStackTrace();
}
...
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>
- (void) parseSomeXML: (NSString *) rawXml {
BOOL success;
NSData *rawXmlConvToData = [rawXml dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *myParser = [[NSXMLParser alloc] initWithData:rawXmlConvToData];
[myParser setShouldResolveExternalEntities:YES];
[myParser setDelegate:self];
}
rawXml
콘텐트를 공격자가 제어할 수 있다고 가정해 보겠습니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
boot.ini
파일의 내용이 포함됩니다.
...
<?php
$goodXML = $_GET["key"];
$doc = simplexml_load_string($goodXml);
echo $doc->testing;
?>
...
Example 2
의 코드에 전달된다고 가정해 봅니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
boot.ini
파일의 내용으로 채워집니다. 공격자는 클라이언트에 반환된 XML 요소를 활용하여 데이터를 몰래 내보내거나 네트워크 리소스의 존재에 관한 정보를 얻을 수 있습니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
/etc/passwd
의 내용을 읽어 문서에 이 내용을 포함합니다.
def readFile() = Action { request =>
val xml = request.cookies.get("doc")
val doc = XMLLoader.loadString(xml)
...
}
func parseXML(xml: String) {
parser = NSXMLParser(data: rawXml.dataUsingEncoding(NSUTF8StringEncoding)!)
parser.delegate = self
parser.shouldResolveExternalEntities = true
parser.parse()
}
rawXml
콘텐트를 공격자가 제어할 수 있다고 가정해 보겠습니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
boot.ini
파일의 내용이 포함됩니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 오버라이드합니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 오버라이드합니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값이 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.
...
<?php
$goodXML = $_GET["key"];
$doc = simplexml_load_string($goodXml);
echo $doc->testing;
?>
...
Example 2
의 코드에 전달된다고 가정해 봅니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
boot.ini
파일의 내용으로 채워집니다. 공격자는 클라이언트에 반환된 XML 요소를 활용하여 데이터를 몰래 내보내거나 네트워크 리소스의 존재에 관한 정보를 얻을 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 재정의합니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.shoes
를 제어할 수 있다고 가정합니다.
<order>
<price>100.00</price>
<item>shoes</item>
</order>
shoes
를 shoes</item><price>1.00</price><item>shoes
로 대체한다고 가정합니다. 새 XML은 다음과 같습니다.
<order>
<price>100.00</price>
<item>shoes</item><price>1.00</price><item>shoes</item>
</order>
<price>
의 값은 첫 번째 <price>
태그의 값을 덮어씁니다. 그러면 공격자는 한 켤레에 $100인 신발을 $1에 구입할 수 있습니다.a:t
태그를 공격자가 제어할 수 있다고 가정해 보겠습니다.
<a:t>YoY results: up 10%</a:t>