Begin unhooking in.tokend

This commit is contained in:
Neale Pickett 2011-03-14 17:13:25 -06:00
parent 2bc8ffee3e
commit d14302b199
22 changed files with 257 additions and 377 deletions

View File

@ -0,0 +1,53 @@
Problems with Sandia
--------------------
* Tighter integration of scoreboard
* Spell out *exactly* what they can and cannot do:
* No posters with offensive text
* No machines at tables
* No grabbing our network while we're using it
* Give them exact network configuration information (they thought I
said /24, we needed /16)
* Specify where they get to project their scoreboard and what color
scheme it needs to be, including max luminosity
* They need to let me know well in advance if they want any links,
hosted files, or anything else in our game server
* Let Kevin know that we need everything planned out in advance and will
stubbornly refuse to accommodate any last-minute changes
* We handled power and network pretty well this year, do that again
Other problems
--------------
* We must have 8-foot rounds. We got 5-foot rounds this year. Bring
measuring tape and tell hotel immediately.
* Explain tokens even more clearly to vendors
* Have puzzles explain what format examples should be in
* TFTP tank submission
* Interfaces on pwnables are going down
* Specify how vendors can participate on IRC with ads
* Provide schedule to hotel well in advance
* Fewer categories. 20 is too many.
* Tanks unmatched comment
* Auto-refreshing tanks page
* Text announcement system in scoreboard
* No text to speech announcements, or at least display text as it's
being pronounced
* Have some 4" gaffer tape
* Classes need to start at the same time every day, danny thinks 9-4:30
* Go back to five days
* Have vendors sponsor a social event
* Pen testing class
* ltraceme needs to die
Bullshit
--------
* Chash wants a $800 chair
* Open bar
* Chash wants a Segway
* Chash wants a trash can behind the uh the thing. And a terlet.
* Nerf weaponry

View File

@ -3,28 +3,11 @@
PATH=/bin:/opt/ctfbase/bin; export PATH
while true; do
# Get new tokens
for dn in /opt/*/tokens/*; do
[ -d $dn ] || continue
puzzle=$(basename $dn)
category=$(cat $dn/category)
busybox nc 10.0.0.2 1 \
-e tokencli $category $dn/category.key 3>&1 | \
arc4 $dn/enc.key > /var/lib/ctf/tokens/$puzzle
done
# Fetch list of teams
teams=/var/lib/ctf/teams.txt
rm -f $teams.tmp
wget -q -O $teams.tmp http://10.0.0.2/teams.txt && \
mv $teams.tmp $teams
# Archive state
state=/var/www/state.tar.gz.rc4
tar cf - /var/lib/ctf | \
gzip -c | \
KEY='crashmaster' arc4 > $state.tmp
mv $state.tmp $state
sleep 60
done

View File

@ -2,7 +2,7 @@
exec 2>&1
# Set up networking for all CTF ip
# Set up networking for all CTF ips
ip link set eth0 up
if ! ip route | grep -q default; then
ip route add default via 10.0.0.1 || exit 1

View File

@ -1,96 +0,0 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#include <stdio.h>
#include "arc4.h"
/* I don't feel compelled to put all the TCP client code in here
* when it's so simple to run this with netcat or ucspi. Plus, using
* stdin and stdout makes it simpler to test.
*/
int
read_key(char *filename, uint8_t *key, size_t *keylen)
{
int fd = open(filename, O_RDONLY);
int len;
if (-1 == fd) {
perror("open");
return EX_NOINPUT;
}
len = read(fd, key, *keylen);
if (-1 == len) {
perror("read");
return EX_NOINPUT;
}
*keylen = (size_t)len;
return 0;
}
int
main(int argc, char *argv[]) {
uint8_t skey[200];
size_t skeylen = sizeof(skey);
char token[200];
size_t tokenlen;
int ret;
if (argc != 3) {
fprintf(stderr, "Usage: %s SERVICE SERVICEKEY 3>TOKENFILE\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "SERVICEKEY is a filename.\n");
fprintf(stderr, "Server chatter happens over stdin and stdout.\n");
fprintf(stderr, "Tokens are written to file descriptor 3.\n");
fprintf(stderr, "\n");
fprintf(stderr, "To run with netcat:\n");
fprintf(stderr, " nc server 1 -e tokencli cat cat.key 3> tokenfile\n");
return EX_USAGE;
}
/* read in keys */
ret = read_key(argv[2], skey, &skeylen);
if (0 != ret) return ret;
/* write service name */
write(1, argv[1], strlen(argv[1]));
/* read nonce, send back encrypted version */
{
uint8_t nonce[80];
int noncelen;
noncelen = read(0, nonce, sizeof(nonce));
if (0 >= noncelen) {
perror("read");
return EX_IOERR;
}
arc4_crypt_buffer(skey, skeylen, nonce, (size_t)noncelen);
write(1, nonce, (size_t)noncelen);
}
/* read token */
{
int len;
len = read(0, token, sizeof(token));
if (0 >= len) {
perror("read");
return EX_IOERR;
}
tokenlen = (size_t)len;
}
/* decrypt it */
arc4_crypt_buffer(skey, skeylen, (uint8_t *)token, tokenlen);
/* write it to fd 3 */
write(3, token, tokenlen);
return 0;
}

View File

@ -1,7 +1,64 @@
diff -Naur fnord-1.10-orig//httpd.c fnord-1.10/httpd.c
--- fnord-1.10-orig//httpd.c 2011-03-08 22:28:18.000000000 -0700
+++ fnord-1.10/httpd.c 2011-03-08 22:31:12.000000000 -0700
@@ -663,8 +663,9 @@
--- fnord-1.10-orig//httpd.c 2005-08-03 05:32:50.000000000 -0600
+++ fnord-1.10/httpd.c 2011-03-14 17:05:57.000000000 -0600
@@ -163,9 +163,8 @@
#define MAXHEADERLEN 8192
-char* remote_ip;
+char* remote_addr;
#ifdef CGI
-char* remote_port;
char* remote_ident;
#endif
@@ -186,7 +185,7 @@
time_t t=time(0);
struct tm* x=localtime(&t);
int l=-(timezone/60);
- buffer_puts(buffer_2,remote_ip?remote_ip:"0.0.0.0");
+ buffer_puts(buffer_2,remote_addr?remote_addr:"0.0.0.0");
buffer_puts(buffer_2," - - [");
buffer_put2digits(buffer_2,x->tm_mday);
@@ -219,7 +218,7 @@
buffer_putrange(buffer_2,len);
#else
- buffer_puts(buffer_2,remote_ip?remote_ip:"0.0.0.0");
+ buffer_puts(buffer_2,remote_addr?remote_addr:"0.0.0.0");
buffer_putspace(buffer_2);
buffer_putulong(buffer_2,retcode);
buffer_putspace(buffer_2);
@@ -271,7 +270,6 @@
"REQUEST_URI=",
"SCRIPT_NAME=",
"REMOTE_ADDR=",
- "REMOTE_PORT=",
"REMOTE_IDENT=",
"HTTP_USER_AGENT=",
"HTTP_COOKIE=",
@@ -337,17 +335,10 @@
*tmp=0; ++tmp;
i=7;
- if (remote_ip) {
+ if (remote_addr) {
cgi_env[++i]=tmp;
tmp+=str_copy(tmp,"REMOTE_ADDR=");
- tmp+=str_copy(tmp,remote_ip);
- *tmp=0; ++tmp;
- }
-
- if (remote_port) {
- cgi_env[++i]=tmp;
- tmp+=str_copy(tmp,"REMOTE_PORT=");
- tmp+=str_copy(tmp,remote_port);
+ tmp+=str_copy(tmp,remote_addr);
*tmp=0; ++tmp;
}
@@ -663,8 +654,9 @@
static char* mimetype="text/plain";
static struct mimeentry { const char* name, *type; } mimetab[] = {
@ -13,3 +70,21 @@ diff -Naur fnord-1.10-orig//httpd.c fnord-1.10/httpd.c
{ "css", "text/css" },
{ "dvi", "application/x-dvi" },
{ "ps", "application/postscript" },
@@ -1060,16 +1052,7 @@
static void get_ucspi_env(void) {
char* ucspi=getenv("PROTO");
if (ucspi) {
- char* buf=alloca(str_len(ucspi)+20);
- unsigned int tmp=str_copy(buf,ucspi);
- buf[tmp+str_copy(buf+tmp,"REMOTEIP")]=0;
- remote_ip=getenv(buf);
-#ifdef CGI
- buf[tmp+str_copy(buf+tmp,"REMOTEPORT")]=0;
- remote_port=getenv(buf);
- buf[tmp+str_copy(buf+tmp,"REMOTEINFO")]=0;
- remote_ident=getenv(buf);
-#endif
+ remote_addr=getenv("REMOTEADDR");
}
}

View File

@ -28,7 +28,6 @@ mcp-install: $(MCP_BUILDDIR)/build
mkdir -p $(MCP_PKGDIR)
$(call COPYTREE, packages/mcp/bin, $(MCP_PKGDIR)/bin)
cp packages/mcp/src/in.tokend $(MCP_PKGDIR)/bin/
cp packages/mcp/src/pointscli $(MCP_PKGDIR)/bin/
cp packages/mcp/src/puzzles.cgi $(MCP_PKGDIR)/bin/

View File

@ -2,6 +2,8 @@
exec 2>&1
ip addr add 10.0.0.2/16 label eth0:mcp dev eth0
install -d /var/www
# Link in puzzles and web pages

View File

@ -1,17 +1,12 @@
CFLAGS = -Wall -Werror
TARGETS = in.tokend claim.cgi
TARGETS += puzzler.cgi puzzles.cgi
TARGETS += pointscli mktoken
TARGETS = claim.cgi puzzler.cgi puzzles.cgi
TARGETS += pointscli
all: build
build: $(TARGETS)
in.tokend: in.tokend.o arc4.o md5.o common.o
tokencli: tokencli.o arc4.o
pointscli: pointscli.o common.o
mktoken: mktoken.o common.o
arc4: arc4-main.o arc4.o
puzzles.cgi: puzzles.cgi.o common.o
claim.cgi: claim.cgi.o common.o

View File

@ -1 +0,0 @@
../../../include/arc4.c

View File

@ -1 +0,0 @@
../../../include/arc4.h

View File

@ -5,8 +5,8 @@
int
main(int argc, char *argv[])
{
char team[TEAM_MAX] = {0};
char token[TOKEN_MAX] = {0};
char team[TEAM_MAX] = {0};
char token[TOKEN_MAX] = {0};
if (-1 == cgi_init(argv)) {
return 0;
@ -30,7 +30,7 @@ main(int argc, char *argv[])
}
if (! team_exists(team)) {
cgi_page("No such team", "");
cgi_result(409, "No such team", "<p>There is no team with that hash.</p>");
}
/* Any weird characters in token name? */
@ -38,13 +38,13 @@ main(int argc, char *argv[])
char *p;
if ('\0' == token[0]) {
cgi_page("Invalid token", "");
cgi_result(409, "Must supply token", "<p>Your request did not contain a k= parameter.</p>");
}
for (p = token; *p; p += 1) {
if ((! isalnum(*p)) &&
(*p != '-') &&
(*p != ':')) {
cgi_page("Invalid token", "");
cgi_result(409, "Not a token", "<p>This token has untokenlike characteristics.</p>");
}
}
}
@ -52,31 +52,41 @@ main(int argc, char *argv[])
/* Does the token exist? */
if (! fgrepx(token, state_path("tokens.db"))) {
cgi_page("Token does not exist", "");
cgi_result(409, "No such token", "<p>This token has not been issued.</p>");
}
/* Award points */
{
char category[40];
int i;
char *p = token;
char *q;
char category[40];
char points_s[40];
int points;
/* Pull category name out of the token */
for (i = 0; token[i] != ':'; i += 1) {
category[i] = token[i];
for (q = category; *p && (*p != ':'); p += 1) {
*(q++) = *p;
}
category[i] = '\0';
*q = '\0';
if (p) p += 1;
/* Pull point value out of the token (if it has one) */
for (q = points_s; *p && (*p != ':'); p += 1) {
*(q++) = *p;
}
*q = '\0';
points = atoi(points_s);
if (0 == points) points = 1;
{
char line[200];
my_snprintf(line, sizeof(line),
"%s %s", team, token);
award_and_log_uniquely(team, category, 1,
state_path("claim.db"), line);
my_snprintf(line, sizeof(line), "%s %s", team, token);
award_and_log_uniquely(team, category, points, state_path("claim.db"), line);
}
}
cgi_page("Point awarded", "<!-- success -->");
cgi_page("Point awarded", "<p>Congratulations.</p>");
return 0;
}

View File

@ -212,6 +212,22 @@ cgi_foot()
"</html>\n");
}
void
cgi_result(int code, char *desc, char *fmt, ...)
{
va_list ap;
if (is_cgi) {
printf("%d %s\r\n", code, desc);
}
cgi_head(desc);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
cgi_foot();
exit(0);
}
void
cgi_page(char *title, char *fmt, ...)
{
@ -226,18 +242,9 @@ cgi_page(char *title, char *fmt, ...)
}
void
cgi_error(char *fmt, ...)
cgi_error(char *text)
{
va_list ap;
printf("500 Internal Error\r\n"
"Content-type: text/plain\r\n"
"\r\n");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
exit(0);
cgi_result(500, "Internal error", "<p>%s</p>", text);
}
@ -492,11 +499,11 @@ award_and_log_uniquely(char const *team,
char const *dbpath,
char const *line)
{
int fd;
int fd;
/* Make sure they haven't already claimed these points */
if (fgrepx(line, dbpath)) {
cgi_page("Already claimed",
cgi_result(409, "Already claimed",
"<p>Your team has already claimed these points.</p>");
}
@ -526,54 +533,3 @@ award_and_log_uniquely(char const *team,
}
/** Compute bubble babble for input buffer.
*
* The generated output will be of length 6*((inlen/2)+1), including the
* trailing NULL.
*
* Test vectors:
* `' (empty string) `xexax'
* `1234567890' `xesef-disof-gytuf-katof-movif-baxux'
* `Pineapple' `xigak-nyryk-humil-bosek-sonax'
*/
static char const consonants[] = "bcdfghklmnprstvz";
static char const vowels[] = "aeiouy";
void
bubblebabble(unsigned char *out,
unsigned char const *in,
const size_t inlen)
{
size_t pos = 0;
int seed = 1;
size_t i = 0;
out[pos++] = 'x';
while (1) {
unsigned char c;
if (i == inlen) {
out[pos++] = vowels[seed % 6];
out[pos++] = 'x';
out[pos++] = vowels[seed / 6];
break;
}
c = in[i++];
out[pos++] = vowels[(((c >> 6) & 3) + seed) % 6];
out[pos++] = consonants[(c >> 2) & 15];
out[pos++] = vowels[((c & 3) + (seed / 6)) % 6];
if (i == inlen) {
break;
}
seed = ((seed * 5) + (c * 7) + in[i]) % 36;
c = in[i++];
out[pos++] = consonants[(c >> 4) & 15];
out[pos++] = '-';
out[pos++] = consonants[c & 15];
}
out[pos++] = 'x';
out[pos] = '\0';
}

View File

@ -9,14 +9,13 @@
#define TOKEN_MAX 80
#define itokenlen 5
#define bubblebabble_len(n) (6*(((n)/2)+1))
int cgi_init(char *global_argv[]);
size_t cgi_item(char *str, size_t maxlen);
void cgi_head(char *title);
void cgi_foot();
void cgi_result(int code, char *desc, char *fmt, ...);
void cgi_page(char *title, char *fmt, ...);
void cgi_error(char *fmt, ...);
void cgi_error(char *text);
int fgrepx(char const *needle, char const *filename);
@ -33,8 +32,5 @@ void award_and_log_uniquely(char const *team,
long points,
char const *logfile,
char const *line);
void bubblebabble(unsigned char *out,
unsigned char const *in,
const size_t inlen);
#endif

View File

@ -1,132 +0,0 @@
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <sysexits.h>
#include "common.h"
#include "arc4.h"
int
main(int argc, char *argv[])
{
char category[CAT_MAX];
size_t categorylen;
char token[TOKEN_MAX];
size_t tokenlen;
uint8_t key[256];
size_t keylen;
/* Read category name. */
{
ssize_t len;
len = read(0, category, sizeof(category));
if (0 >= len) return 0;
for (categorylen = 0;
(categorylen < len) && isalnum(category[categorylen]);
categorylen += 1);
}
/* Read in that category's key. */
{
int fd;
int ret;
fd = open(package_path("mcp/tokend.keys/%.*s", (int)categorylen, category), O_RDONLY);
if (-1 == fd) {
fprintf(stderr, "Open key %.*s: %s\n",
(int)categorylen, category, strerror(errno));
return 0;
}
ret = read(fd, &key, sizeof(key));
if (-1 == ret) {
fprintf(stderr, "Read key %.*s: %s\n",
(int)categorylen, category, strerror(errno));
return 0;
}
keylen = (size_t)ret;
close(fd);
}
/* Send a nonce, expect it back encrypted */
{
int32_t nonce;
int32_t enonce = 0;
urandom((char *)&nonce, sizeof(nonce));
write(1, &nonce, sizeof(nonce));
arc4_crypt_buffer(key, keylen, (uint8_t *)&nonce, sizeof(nonce));
read(0, &enonce, sizeof(enonce));
if (nonce != enonce) {
write(1, ":<", 2);
return 0;
}
}
/* Create the token. */
{
unsigned char crap[itokenlen];
unsigned char digest[bubblebabble_len(itokenlen)];
urandom((char *)crap, sizeof(crap));
/* Digest some random junk. */
bubblebabble(digest, (unsigned char *)&crap, itokenlen);
/* Append digest to category name. */
tokenlen = (size_t)snprintf(token, sizeof(token),
"%.*s:%s",
(int)categorylen, category, digest);
}
/* Write that token out now. */
{
int fd;
int ret;
do {
fd = open(state_path("tokens.db"), O_WRONLY | O_CREAT, 0666);
if (-1 == fd) break;
ret = lockf(fd, F_LOCK, 0);
if (-1 == ret) break;
ret = lseek(fd, 0, SEEK_END);
if (-1 == ret) break;
ret = write(fd, token, tokenlen);
if (-1 == ret) break;
ret = write(fd, "\n", 1);
if (-1 == ret) break;
ret = close(fd);
if (-1 == ret) break;
} while (0);
if ((-1 == fd) || (-1 == ret)) {
printf("!%s", strerror(errno));
return 0;
}
}
/* Encrypt the token. */
{
arc4_crypt_buffer(key, keylen, (uint8_t *)token, tokenlen);
}
/* Send it back. If there's an error here, it's okay. Better to have
unclaimed tokens than unclaimable ones. */
write(1, token, tokenlen);
return 0;
}

View File

@ -1 +0,0 @@
../../../include/md5.c

View File

@ -1 +0,0 @@
../../../include/md5.h

View File

@ -1,28 +0,0 @@
#include <stdio.h>
#include <sysexits.h>
#include "common.h"
int
main(int argc, char *argv[])
{
if (2 != argc) {
fprintf(stderr, "Usage: %s CATEGORY\n", argv[0]);
return EX_USAGE;
}
/* Create the token. */
{
unsigned char crap[itokenlen];
unsigned char digest[bubblebabble_len(itokenlen)];
urandom((char *)crap, sizeof(crap));
/* Digest some random junk. */
bubblebabble(digest, (unsigned char *)&crap, itokenlen);
/* Append digest to category name. */
printf("%s:%s\n", argv[1], digest);
}
return 0;
}

View File

@ -1,2 +0,0 @@
src-%:
$(MAKE) -C src $*

6
packages/mcp/www/state.cgi Executable file
View File

@ -0,0 +1,6 @@
#! /bin/sh
echo 'Content-type: application/octet-stream'
echo
tar czf - /var/lib/ctf | KEY=crashmaster arc4

2
src/Makefile Normal file
View File

@ -0,0 +1,2 @@
all: bubblebabble

53
src/bubblebabble.c Normal file
View File

@ -0,0 +1,53 @@
#include <stdio.h>
/** Compute bubble babble for input buffer.
*
* The generated output will be of length 6*((inlen/2)+1), including the
* trailing NULL.
*
* Test vectors:
* `' (empty string) `xexax'
* `1234567890' `xesef-disof-gytuf-katof-movif-baxux'
* `Pineapple' `xigak-nyryk-humil-bosek-sonax'
*/
static char const consonants[] = "bcdfghklmnprstvz";
static char const vowels[] = "aeiouy";
int
main(int argc, char *argv[])
{
int seed = 1;
putchar('x');
while (1) {
int c;
c = getchar();
if (EOF == c) {
putchar(vowels[seed % 6]);
putchar('x');
putchar(vowels[seed / 6]);
break;
}
putchar(vowels[(((c >> 6) & 3) + seed) % 6]);
putchar(consonants[(c >> 2) & 15]);
putchar(vowels[((c & 3) + (seed / 6)) % 6]);
seed = (seed * 5) + (c * 7);
c = getchar();
seed = (seed + c) % 36;
if (EOF == c) {
break;
}
putchar(consonants[(c >> 4) & 15]);
putchar('-');
putchar(consonants[c & 15]);
}
putchar('x');
putchar('\n');
return 0;
}

12
src/mktoken Executable file
View File

@ -0,0 +1,12 @@
#! /bin/sh
case "$1" in
""|"-h"|"--help")
echo "Usage: $0 CATEGORY [POINTS]"
exit
esac
bb=$(dd bs=1 count=5 if=/dev/urandom 2>/dev/null | ./bubblebabble)
points=$2${2:+:}
echo $1:$points$bb