Ricardo Narvaja’s pwnable – basic exercise 11

The Ricardo Narvaja’s challenge aims to learn about IDA Pro, and use it as much as possible. The challenge can be found here and I modified the IDB for a better understanding.

This challenge interacts with a file called fichero.dat, read and store in local variables (struct). Since is a small file, I can infer that there is a buffer overflow vulnerability on stack in that struct (created in the IDB for me).

Assembly (x86)

1

2

3

4

5

6

7

8

9

10

11

12

00000000estructurastruc; (sizeof=0x52, mappedto_30) ; XREF: main/r

00000000; FF00009F/o

00000000p_bytes_to_readdd?; XREF: vul_func+26/r main+DD/w

00000004bytes_to_readdd?; XREF: main+1B/w main+67/o ...

00000008cookie2 dd?; XREF: main+D/w main+81/o

0000000Creadsdd?; XREF: vul_func+41/w main+74/w ...

00000010cookiedd?; XREF: main+14/w main+9C/o ...

00000014maxsizedd?; XREF: main+6/w main+B0/r

00000018p_systemdd?; XREF: vul_func+14/r main+E6/w ...

0000001Cmem_allocdd?; XREF: main+104/w main+107/r

00000020bufferdb48dup(?); XREF: vul_func+31/o

00000052estructuraends

I’ll show the main pseudocode.

C

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

int__cdecl main(intargc,constchar**argv,constchar**envp)

{

intresult;// eax@2

void*v4;// eax@7

structestructura mi_estructura;// [sp+0h] [bp-58h]@1

structestructura*p_mi_estructura;// [sp+54h] [bp-4h]@3

mi_estructura.maxsize=50;

mi_estructura.cookie2=0;

mi_estructura.cookie=0;

LOBYTE(mi_estructura.bytes_to_read)=0;

File=fopen("fichero.dat","rb");

if(File)

{

p_mi_estructura=&mi_estructura;

mi_estructura.reads=fread(&mi_estructura.bytes_to_read,1u,1u,File);

mi_estructura.reads=fread(&mi_estructura.cookie2,1u,4u,File);

mi_estructura.reads=fread(&mi_estructura.cookie,1u,4u,File);

if(SLOBYTE(mi_estructura.bytes_to_read)<=mi_estructura.maxsize)

{

printf("%08x size :%08x\n",SLOBYTE(mi_estructura.bytes_to_read));

mi_estructura.p_bytes_to_read=(int)&mi_estructura.bytes_to_read;

mi_estructura.p_system=(int)system;

if(mi_estructura.cookie!=623257384)

mi_estructura.p_system=(int)printf;

v4=malloc(0x32u);

mi_estructura.mem_alloc=(int)v4;

MemoryAlloc=(int)v4;

vul_func(p_mi_estructura);

result=0;

}

else

{

perror("Nos fuimos al ******\n");//censured

result=-1;

}

}

else

{

perror("No se puede abrir fichero.dat");

result=-1;

}

returnresult;

}

byte_to_read must be less or equal than max_size, that comparison is interesting because if byte_to_read is a negative number, the condition is met and it continues executing, so if I see the vul_fun function, I can see it is used for reading the file fichero.dat.

So, if I write in byte_to_read the number -1 (0xFF) it will cause a buffer overflow on stack based, but first it needs to bypass all checks in the code. In that function, there is a check, it verifies whether cookie2 is equal 1162233672 (0x45464748). In the epilogue there is also a function called check_bytes_reads which aim is checking whether the bytes read in vul_func are 0x64 bytes.

C

1

2

3

4

5

void__cdecl check_bytes_reads(structestructura*mi_estructura)

{

if(mi_estructura->reads!=100)

exit(1);

}

The bytes reading wich causes a buffer overflow is limited to 0x64 bytes. Now, there is another check in main.

C

24

25

if(mi_estructura.cookie!=623257384)

mi_estructura.p_system=(int)printf;

In this case the system pointer is stored in p_system field, it’s not necessary bypass it, but I think in the next challenges could be necessary for exploitation.

So, with the next three lines and the 64 bytes reading after those bytes, I can bypass all checks.

Now I have made the ROP, but if I look at the stack, there are only 28 bytes, and it needs first an stack pivot. How? Using mnemonics such as: sub esp, r32; xchg esp, r32; etc. In this case, the unique option is sub esp, r32, because I don’t have the stack virtual address.