Welcome to hell
#Welcom to Hell
Introduction
This post is a write-up of a pwn challenge from UMASSCTF 2023.
You can discover a nice obfuscation technic using sys_rt_sigreturn syscall.
You can find le binary here : welcome_to_hell.zip
Discovery
Starting, the program wait from some entry.
./welcome_to_hell
abcdef
# def
bash: def: command not found
It seem read the 3 first characters submitted.
ghidra investigation :
We can see some stranges function names.
The main is this one :
void FUN_6c6c654806b3(void)
{
bool bVar1;
int iVar2;
char *pcVar3;
char local_8;
char local_7;
char acStack_6 [6];
syscall();
iVar2 = 0;
bVar1 = false;
pcVar3 = &local_7;
if (local_8 == '-') {
bVar1 = true;
pcVar3 = acStack_6;
local_8 = local_7;
}
do {
iVar2 = iVar2 * 10 + (int)(char)(local_8 + -0x30);
local_8 = *pcVar3;
if (local_8 < '0') break;
pcVar3 = pcVar3 + 1;
} while (local_8 < ':');
if (bVar1) {
iVar2 = -iVar2;
}
(*(code *)(&LAB_6c6c6548074a + (int)((long)iVar2 * 0x11)))
(0,iVar2,(ulong)((long)iVar2 * 0x11) >> 0x20);
return;
}
not very clear : the code start with syscall() ?
looking the code with radare2 :
┌ 151: entry0 ();
│ ; var int64_t var_8h @ rbp-0x8
│ 0x6c6c654806b3 488bec mov rbp, rsp ; [01] -rwx section size 69766 named .text
│ 0x6c6c654806b6 488d6424f8 lea rsp, [rsp - 8]
│ 0x6c6c654806bb 49b84a074865. movabs r8, sym.L_I ; 0x6c6c6548074a ;
│ 0x6c6c654806c5 488d75f8 lea rsi, [var_8h]
│ 0x6c6c654806c9 33c0 xor eax, eax
│ 0x6c6c654806cb 33ff xor edi, edi
│ 0x6c6c654806cd 6a03 push 3 ; 3
│ 0x6c6c654806cf 5a pop rdx
│ 0x6c6c654806d0 0f05 syscall
│ 0x6c6c654806d2 488d5df8 lea rbx, [var_8h]
│ 0x6c6c654806d6 33f6 xor esi, esi
│ 0x6c6c654806d8 4532db xor r11b, r11b
│ 0x6c6c654806db 4889d8 mov rax, rbx
│ 0x6c6c654806de 90 nop
│ 0x6c6c654806df 90 nop
│ 0x6c6c654806e0 90 nop
│ 0x6c6c654806e1 4883c301 add rbx, 1
│ 0x6c6c654806e5 448a08 mov r9b, byte [rax]
│ 0x6c6c654806e8 4180f92d cmp r9b, 0x2d ; 45
│ ┌─< 0x6c6c654806ec 7402 je 0x6c6c654806f0
│ ┌──< 0x6c6c654806ee eb0e jmp 0x6c6c654806fe
│ ││ ; CODE XREF from entry0 @ 0x6c6c654806ec
│ │└─> 0x6c6c654806f0 6a01 push 1 ; 1
│ │ 0x6c6c654806f2 415b pop r11
│ │ 0x6c6c654806f4 4989da mov r10, rbx
│ │ 0x6c6c654806f7 4883c301 add rbx, 1
│ │ 0x6c6c654806fb 458a0a mov r9b, byte [r10]
│ │ ; CODE XREFS from entry0 @ 0x6c6c654806ee, 0x6c6c6548072e
│ └┌─> 0x6c6c654806fe 6a0a push 0xa ; 10
│ ╎ 0x6c6c65480700 59 pop rcx
│ ╎ 0x6c6c65480701 8bc6 mov eax, esi
│ ╎ 0x6c6c65480703 f7e9 imul ecx
│ ╎ 0x6c6c65480705 4180e930 sub r9b, 0x30 ; 48
│ ╎ 0x6c6c65480709 410fbec9 movsx ecx, r9b
│ ╎ ; CODE XREF from section..strtab @ +0x16d
│ ╎ 0x6c6c6548070d 03c1 add eax, ecx
│ ╎ 0x6c6c6548070f 8bf0 mov esi, eax
│ ╎ 0x6c6c65480711 4989d9 mov r9, rbx
│ ╎ 0x6c6c65480714 4883c301 add rbx, 1
│ ╎ 0x6c6c65480718 458a09 mov r9b, byte [r9]
│ ╎ 0x6c6c6548071b 4180f930 cmp r9b, 0x30 ; 48
│ ╎ ; CODE XREF from section..strtab @ +0x16f
│ ┌──< 0x6c6c6548071f 7c02 jl 0x6c6c65480723
│ ┌───< 0x6c6c65480721 eb07 jmp 0x6c6c6548072a
│ ││╎ ; CODE XREFS from entry0 @ 0x6c6c6548071f, 0x6c6c65480730
│ ┌─└──> 0x6c6c65480723 4584db test r11b, r11b
│ ╎│┌──< 0x6c6c65480726 750a jne 0x6c6c65480732
│ ┌─────< 0x6c6c65480728 eb0e jmp 0x6c6c65480738
│ │╎││╎ ; CODE XREF from entry0 @ 0x6c6c65480721
│ │╎└───> 0x6c6c6548072a 4180f939 cmp r9b, 0x39
│ │╎ │└─< 0x6c6c6548072e 7ece jle 0x6c6c654806fe
│ │└────< 0x6c6c65480730 ebf1 jmp 0x6c6c65480723
│ │ │ ;
│ │ └──> 0x6c6c65480732 8bc6 mov eax, esi
│ │ 0x6c6c65480734 f7d8 neg eax
│ │ 0x6c6c65480736 8bf0 mov esi, eax
│ └─────> 0x6c6c65480738 6a11 push 0x11 ; 17
│ 0x6c6c6548073a 59 pop rcx
│ 0x6c6c6548073b 8bc6 mov eax, esi
│ 0x6c6c6548073d f7e9 imul ecx
│ 0x6c6c6548073f 4863c0 movsxd rax, eax
│ 0x6c6c65480742 4c03c0 add r8, rax ; sym.LI+17
│ 0x6c6c65480745 498bc0 mov rax, r8
└ 0x6c6c65480748 ffe0 jmp rax
The first syscall : is syscall(0,3)
│ 0x6c6c654806c9 33c0 xor eax, eax
│ 0x6c6c654806cb 33ff xor edi, edi
│ 0x6c6c654806cd 6a03 push 3 ; 3
│ 0x6c6c654806cf 5a pop rdx
│ 0x6c6c654806d0 0f05 syscall
After reading 3 cars with syscall(sys_read,3) the program convert it into number and then jmp to : sym.L_I + 17*n, where n is the given number.
The symbols :
fs symbols
[0x6c6c654806b3]> f
0x6c6c654806b3 151 entry0
0x6c6c654806b3 0 sym.Lets_Go__1_1____
0x6c6c6548074a 17 sym.L_I
0x6c6c6548079f 17 sym.small_function
0x6c6c654807b0 17 sym.j2AZE1E1HWHH1___jZAjXI
0x6c6c65480805 17 sym.useful_gadget
0x6c6c65480816 17 sym.L11jXI
0x6c6c6548085a 17 sym._1
0x6c6c6548086b 17 sym.exiting_please
0x6c6c6548087c 17 sym.HjXI
0x6c6c654808c0 17 sym.0
0x6c6c654808d1 17 sym.long_function_name_but_actually_short_function
0x6c6c654808e2 17 sym.1j_1fP_HVHH14_I
0x6c6c65480937 17 sym.shrt_fnc_shrt_nm
0x6c6c65480948 17 sym.H1HHHi
0x6c6c6548097b 17 sym.H1HHHP_
0x6c6c6548098c 17 sym.
0x6c6c6548099d 17 sym.u
0x6c6c654809ae 17 sym.exit_call
0x6c6c654809bf 17 sym.syscall_return
0x6c6c654809d0 17 sym.AAAAA
After the entry point there is one function every 17 bytes from sym.L_I.
┌ 17: sym.L_I ();
│ 0x6c6c6548074a 48c7c7000000. mov rdi, 0
│ 0x6c6c65480751 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c65480758 0f05 syscall
└ 0x6c6c6548075a c3 ret
┌ 17: fcn.6c6c6548075b ();
│ 0x6c6c6548075b 48c7c7010000. mov rdi, 1
│ 0x6c6c65480762 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c65480769 0f05 syscall
└ 0x6c6c6548076b c3 ret
┌ 17: fcn.6c6c6548076c ();
│ 0x6c6c6548076c 48c7c7020000. mov rdi, 2
│ 0x6c6c65480773 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c6548077a 0f05 syscall
└ 0x6c6c6548077c c3 ret
┌ 17: fcn.6c6c6548077d ();
│ 0x6c6c6548077d 48c7c7030000. mov rdi, 3
│ 0x6c6c65480784 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c6548078b 0f05 syscall
└ 0x6c6c6548078d c3 ret
┌ 17: fcn.6c6c6548078e ();
│ 0x6c6c6548078e 48c7c7040000. mov rdi, 4
│ 0x6c6c65480795 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c6548079c 0f05 syscall
└ 0x6c6c6548079e c3 ret
┌ 17: sym.small_function ();
│ 0x6c6c6548079f 48c7c7050000. mov rdi, 5
│ 0x6c6c654807a6 48c7c03c0000. mov rax, 0x3c ; '<' ; 60
│ 0x6c6c654807ad 0f05 syscall
└ 0x6c6c654807af c3 ret
Each entry does a syscall(exit, no) where no is the argument.
With the following script we verify that the entries are regulars.
import struct
def u16(b):
return struct.unpack('<H',b)[0]
fd=open('welcome_to_hell','rb')
fd.read(0x374a)
data=fd.read()
for i in range(0xfff):
entry=data[i*17:(i+1)*17]
if entry[:3].hex() != '48c7c7':
break
if entry[5:].hex() != '000048c7c03c0000000f05c3':
print(i,entry[5:].hex())
break
if u16(entry[3:5]) != i:
print("err indice : ", i)
break
So no way to execute controled action with an ordinary number.
But the code manage the minus (-) symbol, then we can inject a negative value from -1 to -99.
After some tries
./welcome_to_hell
-30
Welcome to the challenge!
Enter the flag:
break on jmp rax in gdb :
gef➤ b *0x6c6c65480748
Breakpoint 1 at 0x6c6c65480748
continue
0x6c6c65480742 add r8, rax
0x6c6c65480745 mov rax, r8
●→ 0x6c6c65480748 jmp rax
0x6c6c6548074a mov rdi, 0x0
0x6c6c65480751 mov rax, 0x3c
with :
$rax : 0x006c6c6548054c
ni
→ 0x6c6c6548054c mov r15, QWORD PTR [rsp+0x10]
0x6c6c65480551 mov r10, 0x0
0x6c6c65480558 jmp 0x6c6c6548056a
ni
$r15 : 0x007ffea8f7de35 → "/work/umasscft2023/welcome_to_hell/welcome_to_hell"
A new sequence :
x/20i 0x6c6c6548056a
=> 0x6c6c6548056a: push 0x32
0x6c6c6548056c: pop r10 ; r10 = 0x32
0x6c6c6548056e: xor r8d,r8d ; r8 = 0
0x6c6c65480571: xor r9d,r9d ; r9=0
0x6c6c65480574: movabs rdi,0x101010101010101
0x6c6c6548057e: push rdi
0x6c6c6548057f: movabs rdi,0x101051515151101
0x6c6c65480589: xor QWORD PTR [rsp],rdi
0x6c6c6548058d: pop rdi ; set rdi=0x41414141000
0x6c6c6548058e: push 0x7
0x6c6c65480590: pop rdx ; rdx = 7
0x6c6c65480591: mov esi,0x1010101
0x6c6c65480596: xor esi,0x1014101 ; set esi=0x4000
0x6c6c6548059c: push 0x9
0x6c6c6548059e: pop rax ; rax=9
0x6c6c6548059f: syscall
0x6c6c654805a1: mov r11,0x0
0x6c6c654805a8: jmp 0x6c6c654805b9
This sequence execute a syscall 0x9 : mmap.
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
Then mmap( 0x41414141000, 0x4000, b111, 0x32, 0,0)
A memory segment is allocated at 0x41414141000 with the size of 0x4000 and rwx permissions.
Before syscall
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000000ffff000 0x00000010000000 0x00000000000000 r-- /work/umasscft2023/welcome_to_hell/welcome_to_hell
0x006c6c65480000 0x006c6c65492000 0x00000000003000 r-x /work/umasscft2023/welcome_to_hell/welcome_to_hell
0x007ffea8f5d000 0x007ffea8f7e000 0x00000000000000 rw- [stack]
0x007ffea8f9e000 0x007ffea8fa2000 0x00000000000000 r-- [vvar]
0x007ffea8fa2000 0x007ffea8fa4000 0x00000000000000 r-x [vdso]
After syscall :
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000000ffff000 0x00000010000000 0x00000000000000 r-- /work/umasscft2023/welcome_to_hell/welcome_to_hell
0x00041414141000 0x00041414145000 0x00000000000000 rwx
0x006c6c65480000 0x006c6c65492000 0x00000000003000 r-x /work/umasscft2023/welcome_to_hell/welcome_to_hell
0x007ffea8f5d000 0x007ffea8f7e000 0x00000000000000 rw- [stack]
0x007ffea8f9e000 0x007ffea8fa2000 0x00000000000000 r-- [vvar]
0x007ffea8fa2000 0x007ffea8fa4000 0x00000000000000 r-x [vdso]
the next segment of execution :
=> 0x6c6c654805b9: mov rdi,r15
“/work/umasscft2023/welcome_to_hell/welcome_to_hell"
0x6c6c654805bc: xor edx,edx
0x6c6c654805be: xor esi,esi
0x6c6c654805c0: push 0x2
0x6c6c654805c2: pop rax
0x6c6c654805c3: syscall ; sys_open
0x6c6c654805c5: mov r11,0x0
0x6c6c654805cc: jmp 0x6c6c654805de
syscall(sys_open,"/work/umasscft2023/welcome_to_hell/welcome_to_hell",0,0)
we can continue to follow the code segment after segment
=> 0x6c6c654805de: mov rdi,rax ; rdi = fd
0x6c6c654805e1: push 0x8
0x6c6c654805e3: pop rax ; rax = sys_seek
0x6c6c654805e4: mov esi,0x1010101
0x6c6c654805e9: xor esi,0x10101b1 ; esi=0xb0
0x6c6c654805ef: cdq
0x6c6c654805f0: syscall
0x6c6c654805f2: mov r13,0x0
0x6c6c654805f9: jmp 0x6c6c6548062b
syscall(sys_seek, 3, 0xb0)
=> 0x6c6c6548062b: xor eax,eax
0x6c6c6548062d: push 0x3
0x6c6c6548062f: pop rdi
0x6c6c65480630: xor edx,edx
0x6c6c65480632: mov dx,0x2f50
0x6c6c65480636: movabs rsi,0x101010101010101
0x6c6c65480640: push rsi
0x6c6c65480641: movabs rsi,0x101051515151101
0x6c6c6548064b: xor QWORD PTR [rsp],rsi
0x6c6c6548064f: pop rsi
0x6c6c65480650: syscall
0x6c6c65480652: mov r14,0x0
0x6c6c65480659: jmp 0x6c6c6548066d
syscall(sys_read, 3,0x41414141000, 0x2f50)
We read 0x2f50 byte from executable offset 0xb0 to the allocated memory segment.
=> 0x6c6c6548066d: xor rcx,rcx
0x6c6c65480670: mov rax,rsi ;rax= 0x41414141000
0x6c6c65480673: sub rsi,0x1
0x6c6c65480677: mov rbx,0x69
0x6c6c6548067e: xor QWORD PTR [rsi+0x1],rbx ; xor [rsi],0x69
0x6c6c65480682: inc rsi
0x6c6c65480685: inc rcx
0x6c6c65480688: cmp rcx,0x2f50 ; loop while rcx<0x2f50
0x6c6c6548068f: jne 0x6c6c6548067e
0x6c6c65480691: jmp rax ; jmp 0x41414141000
This loop xor each byte copied with 0x60 value. Then jump in the beginning :
=> 0x41414141000: and DWORD PTR [rcx-0x137eb71b],ecx
0x41414141006: add BYTE PTR [rip+0x20680000],al # 0x414347c100c
0x4141414100c: or eax,DWORD PTR [rcx]
0x4141414100e: add DWORD PTR [rcx+0x1012434],eax
0x41414141014: add DWORD PTR [rcx],eax
0x41414141016: movabs rax,0x65676e656c6c6168
0x41414141020: push rax
0x41414141021: movabs rax,0x6320656874206f74
0x4141414102b: push rax
0x4141414102c: movabs rax,0x20656d6f636c6557
0x41414141036: push rax
0x41414141037: mov rsi,rsp
0x4141414103a: push 0x1
0x4141414103c: pop rdi
0x4141414103d: push 0x1a
0x4141414103f: pop rdx
0x41414141040: push 0x1
0x41414141042: pop rax
0x41414141043: syscall
syscall(sys_write, 1, ““Welcome to the challenge!\n””, 26)
0x41414141047: dec BYTE PTR [rsp]
0x4141414104a: movabs rax,0x203a67616c662065
0x41414141054: push rax
0x41414141055: movabs rax,0x6874207265746e45
0x41414141045: push
0x4141414105f: push rax
0x41414141060: mov rsi,rsp
0x41414141063: push 0x1
0x41414141065: pop rdi
0x41414141066: push 0x10
0x41414141068: pop rdx
0x41414141069: push 0x1
0x4141414106b: pop rax
0x4141414106c: syscall
syscall(sys_write, 1, “Enter the flag: ”, 16)
0x4141414106e: xor eax,eax ; sys_read
0x41414141070: xor edi,edi ; rdi=0
0x41414141072: push 0x30
0x41414141074: pop rdx ; rdx=0x30
0x41414141075: movabs rsi,0x101010101010101
0x4141414107f: push rsi
0x41414141080: movabs rsi,0x101051515152ed1
0x4141414108a: xor QWORD PTR [rsp],rsi ; 0x00041414142fd0
0x4141414108e: pop rsi
0x4141414108f: syscall
syscall(sys_read,0, 0x00041414142fd0, 0x30)
The program read a flag : UMCTF{AAAAAAAA}
The flag is read to the adresse : 0x00041414142fd0
0x41414141091: mov rdx,rax
0x41414141094: mov rdi,0x19
0x4141414109b: cmp rdx,rdi
0x4141414109e: je 0x414141410a9
0x414141410a0: mov rax,0x3c
0x414141410a7: syscall
If exit(lg) if the entry length is not 0x19 (25) UMCTF{AAAAAAAAAAAAAAAAA}
0x414141410a9: nop
0x414141410aa: lea rsp,[rip+0x9] # 0x414141410ba
0x414141410b1: mov rax,0xf
0x414141410b8: syscall
0x414141410ba: add BYTE PTR [rax],al
0x414141410bc: add BYTE PTR [rax],al
0x414141410be: add BYTE PTR [rax],al
0x414141410c0: add BYTE PTR [rax],al
0x414141410c2: add BYTE PTR [rax],al
0x414141410c4: add BYTE PTR [rax],al
0x414141410c6: add BYTE PTR [rax],al
Then a syscall 0xf is done : sys_rt_sigreturn
Tha man page tell to us :
If the Linux kernel determines that an unblocked signal is pending for a process, then, at the next transition back to user mode in that process (e.g., upon return from a system call or when the process is rescheduled onto the CPU), it creates a new frame on the user-space stack where it saves various pieces of process context (processor status word, registers, signal mask, and signal stack settings).
The kernel also arranges that, during the transition back to user mode, the signal handler is called, and that, upon return from the handler, control passes to a piece of user-space code commonly called the “signal trampoline”. The signal trampoline code in turn calls sigreturn().
sigreturn pop all the registery frame from stack. Here we have , just before the syscall :
0x414141410aa: lea rsp,[rip+0x9] # 0x414141410ba
continue : Just after the syscall(rt_sysret)
$rax : 0xc01f1b3f
$rbx : 0x0
$rcx : 0x0
$rdx : 0x000414141410a0 → 0x0f0000003cc0c748
$rsp : 0x0
$rbp : 0x0
$rsi : 0xffffffffffffffff
$rdi : 0x0
$rip : 0x000414141411b2 → 0x4c01518b4dc90149
$r8 : 0xc01db3af
$r9 : 0x00041414142fcf → "UMCTF{AAAAAAAAAAAAAAAAA}"
$r10 : 0x0
$r11 : 0x0
$r12 : 0x0
$r13 : 0x0
$r14 : 0x0
$r15 : 0x0
The ready to pop frame was :
x/60xg 0x414141410ba
0x414141410ba: 0x0000000000000000 0x0000000000000000
0x414141410ca: 0x0000000000000000 0x0000000000000000
0x414141410da: 0x0000000000000000 r8 0x00000000c01db3af
0x414141410ea: r9 0x0000041414142fcf 0x0000000000000000
0x414141410fa: 0x0000000000000000 0x0000000000000000
0x4141414110a: 0x0000000000000000 0x0000000000000000
0x4141414111a: 0x0000000000000000 0x0000000000000000
0x4141414112a: rsi0xffffffffffffffff 0x0000000000000000
0x4141414113a: 0x0000000000000000 rdx0x00000414141410a0
0x4141414114a: rax0x00000000c01f1b3f 0x0000000000000000
0x4141414115a: 0x0000000000000000 rip0x00000414141411b2
0x4141414116a: 0x0000000000000000 0x0000000000000033
0x4141414117a: 0x0000000000000000 0x0000000000000000
0x4141414118a: 0x0000000000000000 0x0000000000000000
0x4141414119a: 0x0000000000000000 0x0000000000000000
0x414141411aa: 0x0000000000000000 0x4c01518b4dc90149
0x414141411ba: 0xe8d3480bc180c031 0x90e2ff0274d03844
0x414141411ca: 0x4800000009258d48 0x050f0000000fc0c7
0x414141411da: 0x0000000000000000 0x0000000000000000
0x414141411ea: 0x0000000000000000 0x0000000000000000
0x414141411fa: 0x0000000000000000 0x00000000c01db3af
0x4141414120a: 0x0000041414142fcf 0x0000000000000000
0x4141414121a: 0x0000000000000000 0x0000000000000000
0x4141414122a: 0x0000000000000000 0x0000000000000000
0x4141414123a: 0x0000000000000000 0x0000000000000000
0x4141414124a: 0xffffffffffffffff 0x0000000000000000
0x4141414125a: 0x0000000000000000 0x00000414141410a0
0x4141414126a: 0x00000000c0196e6f 0x0000000000000001 rcx
0x4141414127a: 0x0000000000000000 0x00000414141412d2
0x4141414128a: 0x0000000000000000 0x0000000000000033
r9 point then entree flag
=> 0x414141411b2: add r9,rcx
0x414141411b5: mov r10,QWORD PTR [r9+0x1]
0x414141411b9: xor rax,r8
0x414141411bc: add cl,0xb
0x414141411bf: shr rax,cl
0x414141411c2: cmp al,r10b
0x414141411c5: je 0x414141411c9
0x414141411c7: jmp rdx
0x414141411c9: nop
0x414141411ca: lea rsp,[rip+0x9] # 0x414141411da
0x414141411d1: mov rax,0xf
0x414141411d8: syscall
0x414141411da: add BYTE PTR [rax],al
0x414141411dc: add BYTE PTR [rax],al
Next round :
0x414141412d2: add r9,rcx
=> 0x414141412d5: mov r10,QWORD PTR [r9+0x1] ; r10=flag[r9+1]
0x414141412d9: xor rax,r8 ; rax=rax^r8
0x414141412dc: add cl,0xb
0x414141412df: shr rax,cl ; rax=rax >> (cl+11)
0x414141412e2: cmp al,r10b
0x414141412e5: je 0x414141412e9
0x414141412e7: jmp rdx
0x414141412e9: nop
0x414141412ea: lea rsp,[rip+0x9] # 0x414141412fa
0x414141412f1: mov rax,0xf
0x414141412f8: syscall
0x414141412fa: add BYTE PTR [rax],al
On each round n flag[n] is tested as rax^r8 » (11+n). r8 and rax are restored from the frame by syscal(0xf).
The 25 characters of the flag can be calculated by reading the 25 frames. The frame begin in memory at the address 0x414141410ba, at offset 0xba of the loaded segment : 0xb0 + 0xba from start of the executable.
Each segment size is 0x120 where we found r8 at offset 0x28 and rax 0x90.
The xor 0x69 is done twice or each byte for r8 and rax and then neutralized.
Reading the executable whe can extract the 25 couples (r8,rax).
In fact r8 never change and give the fix value : 0x0c01db3af
#Solution
##Script
$ cat extraire_passwd.py
import struct
def u16(b):
return struct.unpack('<H',b)[0]
def u64(b):
return struct.unpack('<Q',b)[0]
fd=open('welcome_to_hell','rb')
fd.read(0xb0)
fd.read(0xba)
data=fd.read()
passwd=''
for i in range(25):
entry=data[i*0x120:(i+1)*0x120]
entry=bytes([ c^0x69 for c in entry])
# print(entry.hex())
r8 = entry[0x28:0x30]
rax= entry[0x90:0x98]
print("r8 ",r8.hex())
print("rax",rax.hex())
rax=bytes([a^b for a,b in zip(rax,r8)])
print(rax.hex())
v=u64(rax)>>(11+i)
passwd+=chr(v)
print(passwd)
Execution
welcome_to_hell$ python3 extraire_passwd.py
r8 afb31dc000000000
rax 3f1b1fc000000000
90a8020000000000
U
r8 afb31dc000000000
rax 6f6e19c000000000
c0dd040000000000
UM
r8 afb31dc000000000
rax 169415c000000000
b927080000000000
UMA
r8 afb31dc000000000
rax 677709c000000000
c8c4140000000000
UMAS
r8 afb31dc000000000
rax a93534c000000000
0686290000000000
UMASS
r8 afb31dc000000000
rax 3cb066c000000000
93037b0000000000
UMASS{
r8 afb31dc000000000
rax 09b8fbc000000000
a60be60000000000
UMASS{s
r8 afb31dc000000000
rax a0b7d5c100000000
0f04c80100000000
UMASS{sr
r8 afb31dc000000000
rax 6bbe9dc100000000
c40d800100000000
UMASS{sr0
r8 afb31dc000000000
rax 34b61dc700000000
9b05000700000000
UMASS{sr0p
r8 afb31dc000000000
rax ebbafdcb00000000
4409e00b00000000
UMASS{sr0p_
r8 afb31dc000000000
rax e0ba9ddb00000000
4f09801b00000000
UMASS{sr0p_n
r8 afb31dc000000000
rax 30b99def00000000
9f0a802f00000000
UMASS{sr0p_n_
r8 afb31dc000000000
rax adb11db200000000
0202007200000000
UMASS{sr0p_n_r
r8 afb31dc000000000
rax 78b31da600000000
d700006600000000
UMASS{sr0p_n_r3
r8 afb31dc000000000
rax 30b71d1801000000
9f0400d801000000
UMASS{sr0p_n_r3v
r8 afb31dc000000000
rax f7be1d3802000000
580d00f802000000
UMASS{sr0p_n_r3v_
r8 afb31dc000000000
rax f5b11d5006000000
5a02009006000000
UMASS{sr0p_n_r3v_i
r8 afb31dc000000000
rax 05be1da00e000000
aa0d00600e000000
UMASS{sr0p_n_r3v_is
r8 afb31dc000000000
rax e9b31d0017000000
460000c017000000
UMASS{sr0p_n_r3v_is_
r8 afb31dc000000000
rax c1b91dc034000000
6e0a000034000000
UMASS{sr0p_n_r3v_is_h
r8 afb31dc000000000
rax 46b31dc033000000
e900000033000000
UMASS{sr0p_n_r3v_is_h3
r8 afb31dc000000000
rax d0b11dc0d8000000
7f020000d8000000
UMASS{sr0p_n_r3v_is_h3l
r8 afb31dc000000000
rax 56ba1dc0b0010000
f9090000b0010000
UMASS{sr0p_n_r3v_is_h3ll
r8 afb31dc000000000
rax 81be1dc0e8030000
2e0d0000e8030000
UMASS{sr0p_n_r3v_is_h3ll}