Skip to content

Commit 09a1288

Browse files
committed
add SECCON2016/tinypad/src and binary built myself
1 parent d64f0b0 commit 09a1288

File tree

7 files changed

+365
-0
lines changed

7 files changed

+365
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ heap/*
33
peda-session-*.txt
44
.gdb_history
55
*.sw[po]
6+
*.idb
7+
*.i64
8+
.DS_Store

SECCON2016/tinypad/src/Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CC = gcc
2+
CFLAGS =-c -std=gnu11 -fstack-protector-all -fPIC
3+
LDFLAGS = -Wl,-z,now -Wl,-z,relro #-pie
4+
5+
sources = tinypad.c pwnio.c
6+
objects = $(sources:.c=.o)
7+
solution= tinypad
8+
9+
.PHONY: clean
10+
all: $(sources) $(solution)
11+
12+
$(solution): $(objects)
13+
$(CC) $(LDFLAGS) $(objects) -o $@
14+
.c.o:
15+
$(CC) $(CFLAGS) $< -o $@
16+
17+
clean:
18+
rm $(objects) $(solution)

SECCON2016/tinypad/src/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# tinypad
2+
3+
__keywords__: heap exploitation, glibc, Use After Free, malloc\_consolidate, poisoned NUL byte, House of Einherjar
4+
5+
## What's This?
6+
"tinypad" is a pwnable challenge for SECCON 2016 Online CTF as pwn300.
7+
8+
## My Intended Solution
9+
After analysis, we can get two vulnerabilities, Use After Free(leak only) and Non-NUL terminated string.
10+
A fastbin-sized free()'d chunk and one smallbin-sized `malloc()` lead a fastbin into `unsorted_chunks` so we can leak the address of `main_arena` and calculate the base address for libc.
11+
Now, we can corrupt a chunk size by writing into Non-NULL terminated string but we are not able to use House of Force due to limited request size for an allocation. House of Einherjar is suitable in this case.
12+
Now, we can forge the list of pads and get an arbitrary memory read and a partial write.
13+
14+
It's enough to write up. See the detail in "exploit\_tinypad.py".
15+
16+
___Good pwn time,___
17+
@hhc0null

SECCON2016/tinypad/src/pwnio.c

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include "pwnio.h"
2+
3+
static inline ssize_t _read_n(int fd, char *buf, size_t n)
4+
{
5+
if(buf == NULL) return -1;
6+
if(n == 0) return 0;
7+
8+
ssize_t rx = 0;
9+
10+
while(rx < n) {
11+
ssize_t r = read(fd, buf + rx, n - rx);
12+
if(r < 0) {
13+
// interupted or under non-block?
14+
if(errno == EAGAIN || errno == EINTR) continue;
15+
// another reason.
16+
return -1;
17+
}
18+
// sending finished.
19+
if(r == 0) break;
20+
rx += r;
21+
}
22+
23+
return rx;
24+
}
25+
26+
static inline ssize_t _write_n(int fd, const char *buf, size_t n)
27+
{
28+
if(buf == NULL) return -1;
29+
if(n == 0) return 0;
30+
31+
ssize_t tx = 0;
32+
33+
while(tx < n) {
34+
ssize_t r = write(fd, buf+tx, n - tx);
35+
if(r < 0) {
36+
// interupted or under non-block?
37+
if(errno == EAGAIN || errno == EINTR) continue;
38+
// another reason.
39+
return -1;
40+
}
41+
// sending finished.
42+
if(r == 0) break;
43+
tx += r;
44+
}
45+
46+
return tx;
47+
}
48+
49+
static inline void _dummyinput(int c)
50+
{
51+
if(!c) return;
52+
char dummy = '\0';
53+
while(dummy != c)
54+
read_n(&dummy, 1);
55+
}
56+
57+
extern ssize_t read_n(char *buf, size_t n)
58+
{
59+
return _read_n(STDIN_FILENO, buf, n);
60+
}
61+
62+
extern ssize_t read_until(char *buf, size_t n, int c)
63+
{
64+
size_t rx = 0;
65+
66+
while(rx < n) {
67+
ssize_t r = _read_n(STDIN_FILENO, buf + rx, 1);
68+
if(r < 0) return -1;
69+
if(r == 0 || buf[rx] == c) break;
70+
rx += 1;
71+
}
72+
buf[rx] = '\0';
73+
74+
if(rx == n && buf[n-1] != '\n')
75+
_dummyinput(c);
76+
77+
return rx;
78+
}
79+
80+
extern ssize_t write_n(const char *buf, size_t n)
81+
{
82+
return _write_n(STDOUT_FILENO, buf, n);
83+
}
84+
85+
extern ssize_t write_errn(const char *buf, size_t n)
86+
{
87+
return _write_n(STDERR_FILENO, buf, n);
88+
}
89+
90+
extern ssize_t writeln(const char *buf, size_t n)
91+
{
92+
ssize_t sum = 0;
93+
sum += write_n(buf, n);
94+
sum += write_n("\n", 1);
95+
return sum;
96+
}
97+
98+
extern ssize_t writerrln(const char *buf, size_t n)
99+
{
100+
ssize_t sum = 0;
101+
sum += write_errn(buf, n);
102+
sum += write_errn("\n", 1);
103+
return sum;
104+
}
105+
106+
extern int read_int(void)
107+
{
108+
char buf[18] = {};
109+
read_until(buf, (sizeof(buf)/sizeof(buf[0])) - 1, '\n');
110+
return atoi(buf);
111+
}

SECCON2016/tinypad/src/pwnio.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef _LOWLEVEL_H
2+
# define _LOWLEVEL_H
3+
#include <errno.h>
4+
#include <fcntl.h>
5+
#include <stdbool.h>
6+
#include <stdlib.h>
7+
#include <unistd.h>
8+
9+
extern ssize_t read_n(char *buf, size_t n);
10+
extern ssize_t read_until(char *buf, size_t n, int c);
11+
extern ssize_t write_n(const char *buf, size_t n);
12+
extern ssize_t write_errn(const char *buf, size_t n);
13+
extern ssize_t writeln(const char *buf, size_t n);
14+
extern ssize_t writerrln(const char *buf, size_t n);
15+
extern int read_int(void);
16+
#endif

SECCON2016/tinypad/src/tinypad.c

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#include <ctype.h>
2+
#include <errno.h>
3+
#include <signal.h>
4+
#include <stdlib.h>
5+
#include <stdio.h>
6+
#include <string.h>
7+
#include "pwnio.h"
8+
9+
#define PADSIZE 0x4
10+
const char title[] = " ============================================================================\n"
11+
"// _|_|_|_|_| _|_|_| _| _| _| _| _|_|_| _|_| _|_|_| \\\\\n"
12+
"|| _| _| _|_| _| _| _| _| _| _| _| _| _| ||\n"
13+
"|| _| _| _| _| _| _| _|_|_| _|_|_|_| _| _| ||\n"
14+
"|| _| _| _| _|_| _| _| _| _| _| _| ||\n"
15+
"\\\\ _| _|_|_| _| _| _| _| _| _| _|_|_| //\n"
16+
" ============================================================================\n";
17+
const char separator[] = "+------------------------------------------------------------------------------+\n";
18+
const char menu[] = "+- MENU -----------------------------------------------------------------------+\n"
19+
"| [A] Add memo |\n"
20+
"| [D] Delete memo |\n"
21+
"| [E] Edit memo |\n"
22+
"| [Q] Quit |\n"
23+
"+------------------------------------------------------------------------------+\n";
24+
25+
const char show_index[] = " # INDEX: ";
26+
const char show_content[] = " # CONTENT: ";
27+
const char confirm_content[] = "CONTENT: ";
28+
29+
const char prompt_cmd[] = "(CMD)>>> ";
30+
const char prompt_size[] = "(SIZE)>>> ";
31+
const char prompt_content[] = "(CONTENT)>>> ";
32+
const char prompt_index[] = "(INDEX)>>> ";
33+
const char prompt_confirm[] = "(Y/n)>>> ";
34+
35+
const char errmsg_no_space_left[] = "No space is left.";
36+
const char errmsg_no_such_command[] = "No such a command";
37+
const char errmsg_invalid_index[] = "Invalid index";
38+
const char errmsg_not_used[] = "Not used";
39+
40+
const char syserr_no_memory_is_available[] = "[!] No memory is available.";
41+
const char syserr_init_failed[] = "[!] Init failed.";
42+
const char msg_confirm[] = "Is it OK?";
43+
const char msg_timeout[] = "Timeout.";
44+
45+
const size_t memo_maxlen = 0x100;
46+
struct {
47+
char buffer[0x100]; // make a fakechunk.
48+
struct {
49+
size_t size;
50+
char *memo;
51+
} page[4];
52+
} tinypad;
53+
54+
static inline void dummyinput(int c)
55+
{
56+
if(!c) return;
57+
char dummy = '\0';
58+
while(dummy != c)
59+
read_n(&dummy, 1);
60+
}
61+
62+
63+
int getcmd()
64+
{
65+
int cmd = '\0';
66+
67+
write_n(menu, strlen(menu));
68+
69+
write_n(prompt_cmd, strlen(prompt_cmd));
70+
read_until((char *)&cmd, 1, '\n');
71+
write_n("\n", 1);
72+
73+
return toupper(cmd);
74+
}
75+
76+
void sighandler(int signum)
77+
{
78+
writeln("\n", 1);
79+
switch(signum) {
80+
case SIGALRM:
81+
writeln(msg_timeout, strlen(msg_timeout));
82+
break;
83+
}
84+
_exit(0);
85+
}
86+
87+
void init()
88+
{
89+
if(signal(SIGALRM, sighandler) < 0) {
90+
goto LABEL_INIT_FAILED;
91+
}
92+
alarm(30);
93+
94+
return;
95+
96+
LABEL_INIT_FAILED:
97+
writerrln(syserr_init_failed, strlen(syserr_init_failed));
98+
_exit(-1);
99+
}
100+
101+
int main()
102+
{
103+
int cmd = '\0';
104+
105+
init();
106+
107+
write_n("\n", 1);
108+
write_n(title, strlen(title));
109+
write_n("\n", 1);
110+
do{
111+
for(int i = 0; i < PADSIZE; i++) {
112+
char count = '1'+i;
113+
writeln(separator, strlen(separator));
114+
115+
write_n(show_index, strlen(show_index)); writeln(&count, 1);
116+
write_n(show_content, strlen(show_content));
117+
if(tinypad.page[i].memo) {
118+
writeln(tinypad.page[i].memo, strlen(tinypad.page[i].memo));
119+
}
120+
writeln("\n", 1);
121+
}
122+
int idx = 0;
123+
switch(cmd = getcmd()) {
124+
case 'A': {
125+
while(idx < PADSIZE && tinypad.page[idx].size != 0) idx++;
126+
if(idx == PADSIZE) {
127+
writeln(errmsg_no_space_left, strlen(errmsg_no_space_left));
128+
break;
129+
}
130+
int size = -1;
131+
write_n(prompt_size, strlen(prompt_size));
132+
size = read_int();
133+
size = (size < 0x1)? 0x1:
134+
(size < memo_maxlen)? size: memo_maxlen;
135+
tinypad.page[idx].size = size;
136+
137+
if((tinypad.page[idx].memo = malloc(size)) == NULL) {
138+
writerrln("[!] No memory is available.", strlen("[!] No memory is available."));
139+
_exit(-1);
140+
}
141+
142+
write_n(prompt_content, strlen(prompt_content));
143+
read_until(tinypad.page[idx].memo, size, '\n');
144+
writeln("\nAdded.", strlen("\nAdded."));
145+
} break;
146+
case 'D': {
147+
write_n(prompt_index, strlen(prompt_index));
148+
idx = read_int();
149+
if(!(0 < idx && idx <= PADSIZE)) {
150+
writeln(errmsg_invalid_index, strlen(errmsg_invalid_index));
151+
break;
152+
}
153+
if(tinypad.page[idx-1].size == 0) {
154+
writeln(errmsg_not_used, strlen(errmsg_not_used));
155+
break;
156+
}
157+
158+
// XXX: UAF
159+
free(tinypad.page[idx-1].memo);
160+
tinypad.page[idx-1].size = 0;
161+
162+
writeln("\nDeleted.", strlen("\nDeleted."));
163+
} break;
164+
case 'E': {
165+
write_n(prompt_index, strlen(prompt_index));
166+
idx = read_int();
167+
if(!(0 < idx && idx <= PADSIZE)) {
168+
writeln(errmsg_invalid_index, strlen(errmsg_invalid_index));
169+
break;
170+
}
171+
if(tinypad.page[idx-1].size == 0) {
172+
writeln(errmsg_not_used, strlen(errmsg_not_used));
173+
break;
174+
}
175+
176+
int confirmation = '0';
177+
strcpy(tinypad.buffer, tinypad.page[idx-1].memo);
178+
while(toupper(confirmation) != 'Y') {
179+
write_n(confirm_content, strlen(confirm_content));
180+
writeln(tinypad.buffer, strlen(tinypad.buffer));
181+
write_n(prompt_content, strlen(prompt_content));
182+
// XXX: Not NUL Terminated.
183+
read_until(tinypad.buffer, strlen(tinypad.page[idx-1].memo), '\n');
184+
writeln(msg_confirm, strlen(msg_confirm));
185+
write_n(prompt_confirm, strlen(prompt_confirm));
186+
read_until((char *)&confirmation, 1, '\n');
187+
}
188+
strcpy(tinypad.page[idx-1].memo, tinypad.buffer);
189+
190+
writeln("\nEdited.", strlen("\nEdited."));
191+
} break;
192+
default:
193+
writeln(errmsg_no_such_command, strlen(errmsg_no_such_command));
194+
case 'Q':
195+
break;
196+
}
197+
} while(cmd != 'Q');
198+
199+
return 0;
200+
}

SECCON2016/tinypad/tinypad

17.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)