Contents

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}