diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md
index a2af62646..bf6ec1860 100644
--- a/docs/_docs/user-guide/eldritch.md
+++ b/docs/_docs/user-guide/eldritch.md
@@ -548,3 +548,45 @@ The crypto.hash_file method will produce the hash of the given file's con
- SHA1
- SHA256
- SHA512
+
+### crypto.encode_b64
+`crypto.encode_b64(content: str, encode_type: Optional) -> str`
+
+The crypto.encode_b64 method encodes the given text using the given base64 encoding method. Valid methods include:
+
+- STANDARD (default)
+- STANDARD_NO_PAD
+- URL_SAFE
+- URL_SAFE_NO_PAD
+
+### crypto.decode_b64
+`crypto.decode_b64(content: str, decode_type: Optional) -> str`
+
+The crypto.decode_b64 method encodes the given text using the given base64 decoding method. Valid methods include:
+
+- STANDARD (default)
+- STANDARD_NO_PAD
+- URL_SAFE
+- URL_SAFE_NO_PAD
+
+### crypto.from_json
+`crypto.from_json(content: str) -> Value`
+
+The crypto.from_json method converts JSON text to an object of correct type.
+
+```python
+crypto.from_json("{\"foo\":\"bar\"}")
+{
+ "foo": "bar"
+}
+```
+
+### crypto.to_json
+`crypto.to_json(content: Value) -> str`
+
+The crypto.to_json method converts given type to JSON text.
+
+```python
+crypto.to_json({"foo": "bar"})
+"{\"foo\":\"bar\"}"
+```
diff --git a/implants/Cargo.toml b/implants/Cargo.toml
index fff2084f0..58e242fd9 100644
--- a/implants/Cargo.toml
+++ b/implants/Cargo.toml
@@ -14,6 +14,7 @@ anyhow = "1.0.65"
assert_cmd = "2.0.6"
async-recursion = "1.0.0"
async-trait = "0.1.68"
+base64 = "0.21.4"
chrono = "0.4.24"
clap = "3.2.23"
default-net = "0.13.1"
diff --git a/implants/lib/eldritch/Cargo.toml b/implants/lib/eldritch/Cargo.toml
index 68d532a52..0f956c5f7 100644
--- a/implants/lib/eldritch/Cargo.toml
+++ b/implants/lib/eldritch/Cargo.toml
@@ -10,6 +10,7 @@ allocative_derive = { workspace = true }
anyhow = { workspace = true }
async-recursion = { workspace = true }
async-trait = { workspace = true }
+base64 = { workspace = true }
chrono = { workspace = true }
derive_more = { workspace = true }
eval = { workspace = true }
diff --git a/implants/lib/eldritch/src/crypto.rs b/implants/lib/eldritch/src/crypto.rs
index 94c5aae37..8141552a3 100644
--- a/implants/lib/eldritch/src/crypto.rs
+++ b/implants/lib/eldritch/src/crypto.rs
@@ -1,6 +1,10 @@
mod aes_encrypt_file_impl;
mod aes_decrypt_file_impl;
mod hash_file_impl;
+mod encode_b64_impl;
+mod decode_b64_impl;
+mod from_json_impl;
+mod to_json_impl;
use allocative::Allocative;
use derive_more::Display;
@@ -64,4 +68,20 @@ fn methods(builder: &mut MethodsBuilder) {
if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); }
hash_file_impl::hash_file(file, algo)
}
+ fn encode_b64<'v>(this: CryptoLibrary, content: String, encode_type: Option) -> anyhow::Result {
+ if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); }
+ encode_b64_impl::encode_b64(content, encode_type)
+ }
+ fn decode_b64<'v>(this: CryptoLibrary, content: String, encode_type: Option) -> anyhow::Result {
+ if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); }
+ decode_b64_impl::decode_b64(content, encode_type)
+ }
+ fn from_json<'v>(this: CryptoLibrary, starlark_heap: &'v Heap, content: String) -> anyhow::Result> {
+ if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); }
+ from_json_impl::from_json(starlark_heap, content)
+ }
+ fn to_json<'v>(this: CryptoLibrary, content: Value) -> anyhow::Result {
+ if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); }
+ to_json_impl::to_json(content)
+ }
}
diff --git a/implants/lib/eldritch/src/crypto/decode_b64_impl.rs b/implants/lib/eldritch/src/crypto/decode_b64_impl.rs
new file mode 100644
index 000000000..dbace2a8f
--- /dev/null
+++ b/implants/lib/eldritch/src/crypto/decode_b64_impl.rs
@@ -0,0 +1,51 @@
+use anyhow::{anyhow, Result};
+use base64::{Engine, engine::general_purpose};
+
+pub fn decode_b64(content: String, encode_type: Option) -> Result {
+ let decode_type = match encode_type.unwrap_or("STANDARD".to_string()).as_str() {
+ "STANDARD" => {general_purpose::STANDARD},
+ "STANDARD_NO_PAD" => {general_purpose::STANDARD_NO_PAD},
+ "URL_SAFE" => {general_purpose::URL_SAFE},
+ "URL_SAFE_NO_PAD" => {general_purpose::URL_SAFE_NO_PAD},
+ _ => return Err(anyhow!("Invalid encode type. Valid types are: STANDARD, STANDARD_NO_PAD, URL_SAFE_PAD, URL_SAFE_NO_PAD"))
+ };
+ decode_type.decode(content.as_bytes()).map(|res| String::from_utf8_lossy(&res).to_string()).map_err(|e| anyhow!("Error decoding base64: {:?}", e))
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_decode_b64() -> anyhow::Result<()>{
+ let res = super::decode_b64("dGVzdA==".to_string(), Some("STANDARD".to_string()))?;
+ assert_eq!(res, "test");
+ let res = super::decode_b64("dGVzdA".to_string(), Some("STANDARD_NO_PAD".to_string()))?;
+ assert_eq!(res, "test");
+ let res = super::decode_b64("aHR0cHM6Ly9nb29nbGUuY29tLyY=".to_string(), Some("URL_SAFE".to_string()))?;
+ assert_eq!(res, "https://google.com/&");
+ let res = super::decode_b64("aHR0cHM6Ly9nb29nbGUuY29tLyY".to_string(), Some("URL_SAFE_NO_PAD".to_string()))?;
+ assert_eq!(res, "https://google.com/&");
+ Ok(())
+ }
+
+ #[test]
+ fn test_decode_b64_invalid_type() -> anyhow::Result<()>{
+ let res = super::decode_b64("test".to_string(), Some("INVALID".to_string()));
+ assert!(res.is_err());
+ Ok(())
+ }
+
+ #[test]
+ fn test_decode_b64_default_type() -> anyhow::Result<()>{
+ let res = super::decode_b64("dGVzdA==".to_string(), None)?;
+ assert_eq!(res, "test");
+ Ok(())
+ }
+
+ #[test]
+ fn test_decode_b64_invalid_content() -> anyhow::Result<()>{
+ let res = super::decode_b64("///".to_string(), Some("STANDARD".to_string()));
+ assert!(res.is_err());
+ Ok(())
+ }
+
+}
diff --git a/implants/lib/eldritch/src/crypto/encode_b64_impl.rs b/implants/lib/eldritch/src/crypto/encode_b64_impl.rs
new file mode 100644
index 000000000..b4b974d28
--- /dev/null
+++ b/implants/lib/eldritch/src/crypto/encode_b64_impl.rs
@@ -0,0 +1,43 @@
+use anyhow::{anyhow, Result};
+use base64::{Engine, engine::general_purpose};
+
+pub fn encode_b64(content: String, encode_type: Option) -> Result {
+ let encode_type = match encode_type.unwrap_or("STANDARD".to_string()).as_str() {
+ "STANDARD" => {general_purpose::STANDARD},
+ "STANDARD_NO_PAD" => {general_purpose::STANDARD_NO_PAD},
+ "URL_SAFE" => {general_purpose::URL_SAFE},
+ "URL_SAFE_NO_PAD" => {general_purpose::URL_SAFE_NO_PAD},
+ _ => return Err(anyhow!("Invalid encode type. Valid types are: STANDARD, STANDARD_NO_PAD, URL_SAFE_PAD, URL_SAFE_NO_PAD"))
+ };
+ Ok(encode_type.encode(content.as_bytes()))
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_encode_b64() -> anyhow::Result<()>{
+ let res = super::encode_b64("test".to_string(), Some("STANDARD".to_string()))?;
+ assert_eq!(res, "dGVzdA==");
+ let res = super::encode_b64("test".to_string(), Some("STANDARD_NO_PAD".to_string()))?;
+ assert_eq!(res, "dGVzdA");
+ let res = super::encode_b64("https://google.com/&".to_string(), Some("URL_SAFE".to_string()))?;
+ assert_eq!(res, "aHR0cHM6Ly9nb29nbGUuY29tLyY=");
+ let res = super::encode_b64("https://google.com/&".to_string(), Some("URL_SAFE_NO_PAD".to_string()))?;
+ assert_eq!(res, "aHR0cHM6Ly9nb29nbGUuY29tLyY");
+ Ok(())
+ }
+
+ #[test]
+ fn test_encode_b64_invalid_type() -> anyhow::Result<()>{
+ let res = super::encode_b64("test".to_string(), Some("INVALID".to_string()));
+ assert!(res.is_err());
+ Ok(())
+ }
+
+ #[test]
+ fn test_encode_b64_default_type() -> anyhow::Result<()>{
+ let res = super::encode_b64("test".to_string(), None)?;
+ assert_eq!(res, "dGVzdA==");
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/implants/lib/eldritch/src/crypto/from_json_impl.rs b/implants/lib/eldritch/src/crypto/from_json_impl.rs
new file mode 100644
index 000000000..fd38b9d65
--- /dev/null
+++ b/implants/lib/eldritch/src/crypto/from_json_impl.rs
@@ -0,0 +1,40 @@
+use anyhow::{anyhow, Result};
+use starlark::values::{Heap, Value};
+
+pub fn from_json(starlark_heap: &Heap, json: String) -> Result {
+ let json_data: serde_json::Value = serde_json::from_str(&json).map_err(|e| anyhow!("Error parsing json: {:?}", e))?;
+
+ Ok(starlark_heap.alloc(json_data.clone()))
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json::json;
+ use starlark::values::{Heap, Value};
+
+ #[test]
+ fn test_from_json_object() -> anyhow::Result<()>{
+ let test_heap = Heap::new();
+ let res = super::from_json(&test_heap, r#"{"test": "test"}"#.to_string())?;
+ let res_value = test_heap.alloc(json!({"test": "test"}));
+ assert_eq!(res, res_value);
+ Ok(())
+ }
+
+ #[test]
+ fn test_from_json_list() -> anyhow::Result<()>{
+ let test_heap = Heap::new();
+ let res = super::from_json(&test_heap, r#"[1, "foo", false, null]"#.to_string())?;
+ let res_value = test_heap.alloc(json!([1, "foo", false, null]));
+ assert_eq!(res, res_value);
+ Ok(())
+ }
+
+ #[test]
+ fn test_from_json_invalid() -> anyhow::Result<()>{
+ let test_heap = Heap::new();
+ let res = super::from_json(&test_heap, r#"{"test":"#.to_string());
+ assert!(res.is_err());
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/implants/lib/eldritch/src/crypto/to_json_impl.rs b/implants/lib/eldritch/src/crypto/to_json_impl.rs
new file mode 100644
index 000000000..d884c8b91
--- /dev/null
+++ b/implants/lib/eldritch/src/crypto/to_json_impl.rs
@@ -0,0 +1,40 @@
+use anyhow::Result;
+use starlark::values::Value;
+
+pub fn to_json(json: Value) -> Result {
+ json.to_json()
+}
+
+#[cfg(test)]
+mod tests {
+ use starlark::{values::{dict::Dict, Heap, Value}, const_frozen_string, collections::SmallMap};
+ use anyhow::Result;
+
+ #[test]
+ fn to_json_object() -> Result<()> {
+ let test_heap = Heap::new();
+ let res = SmallMap::new();
+ let mut dict_res = Dict::new(res);
+ dict_res.insert_hashed(
+ const_frozen_string!("test").to_value().get_hashed()?,
+ test_heap.alloc_str("test").to_value(),
+ );
+ let res = super::to_json(test_heap.alloc(dict_res))?;
+ assert_eq!(res, r#"{"test":"test"}"#);
+ Ok(())
+ }
+
+ #[test]
+ fn to_json_list() -> Result<()> {
+ let test_heap = Heap::new();
+ let mut vec_val: Vec = Vec::new();
+ vec_val.push(test_heap.alloc(1));
+ vec_val.push(test_heap.alloc("foo"));
+ vec_val.push(test_heap.alloc(false));
+ vec_val.push(Value::new_none());
+ let res = test_heap.alloc(vec_val);
+ let res = super::to_json(res)?;
+ assert_eq!(res, r#"[1,"foo",false,null]"#);
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs
index 9fe8dab21..d73a88ce8 100644
--- a/implants/lib/eldritch/src/lib.rs
+++ b/implants/lib/eldritch/src/lib.rs
@@ -193,7 +193,7 @@ dir(process) == ["kill", "list", "name"]
dir(sys) == ["dll_inject", "exec", "get_env", "get_ip", "get_os", "get_pid", "get_user", "is_linux", "is_macos", "is_windows", "shell"]
dir(pivot) == ["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "smb_exec", "ssh_copy", "ssh_exec", "ssh_password_spray"]
dir(assets) == ["copy","list","read","read_binary"]
-dir(crypto) == ["aes_decrypt_file", "aes_encrypt_file", "hash_file"]
+dir(crypto) == ["aes_decrypt_file", "aes_encrypt_file", "decode_b64", "encode_b64", "from_json", "hash_file", "to_json"]
"#,
);
}