notes on death note (pwnable.tw)

This question seems to be longer than the last apple store from the first time I saw the title… The impression seems to be the weekend exercise. Then I would not write shellcode at the time. I gave up until now… I think I am more suitable for playing wargame, the time limit of CTF is so tired…

There is nothing to review this question, even if I have had it for a long time, I forgot.

The problem is not long, the loopholes are obvious (I always think that the vulnerability is significantly related to the amount of code in the title…), but the card is not written in the alphanumeric shellcode (it should be said that the original shellcode I wrote is not what it is.)

At that time, I was on the net to find out if there was a shellcode available for use. I found a few impressions, but it was not too long to conform to the problem environment.

I recently made a report to tell the shellcode to see tutorials. After the understanding of the temperament, I found that it was not so difficult.

The topic information is as follows:

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

A short menu title with add show delete function:

int add_note()
{
  int v1; // [sp+8h] [bp-60h]@1
  char s; // [sp+Ch] [bp-5Ch]@4
  int v3; // [sp+5Ch] [bp-Ch]@1

  v3 = *MK_FP(__GS__, 20);
  printf("Index :");
  v1 = read_int();
  if ( v1 > 10 )
  {
    puts("Out of bound !!");
    exit(0);
  }
  printf("Name :");
  read_input(&s, 0x50u);
  if ( !is_printable(&s) )
  {
    puts("It must be a printable name !");
    exit(-1);
  }
  *(&note + v1) = strdup(&s); //vulnerability!!
  puts("Done !");
  return *MK_FP(__GS__, 20) ^ v3;
}

int del_note()
{
  int result; // eax@4
  int v1; // [sp+Ch] [bp-Ch]@1

  printf("Index :");
  v1 = read_int();
  if ( v1 > 10 )
  {
    puts("Out of bound !!");
    exit(0);
  }
  free(*(&note + v1));
  result = v1;
  *(&note + v1) = 0;
  return result;
}


int show_note()
{
  int result; // eax@4
  int v1; // [sp+Tr] [bp-Ch]@1

  printf("Index :");
  v1 = read_int();
  if ( v1 > 10 )
  {
    puts("Out of bound !!");
    exit(0);
  }
  result = (int)*(&note + v1);
  if ( result )
    result = printf("Name : %s\n", *(&note + v1));
  return result;
}

The location of the vulnerability is calculated, no matter what function, you have to enter the index where there are cross-border read and write vulnerabilities, plus the address of the notes on the bss, through the index of the cross-border vulnerability can cover the libc address on the got, let It points to the heap, and the program does not have NX, so you can directly write shellcode to jump over the execution.

I’ve found it very well here, I’ve seen it before, and the trouble is the alphanumeric shellcode behind.

Alphanumeric shellcode, as the name suggests, simply writes shellcode with A~Za~z0~9, but this question is not alphanumeric shellcode, it should be called Printable shellcode, cuz other ascii visual characters can also be used, so there is more than pure alphanumeric shellcode. Less things can be used, I have some useful printable byte to create shellcode.

Here is the function that he judges whether it is printable, after the result of ida decompile.

int __cdecl is_printable(char *s)
{
  size_t i; // [sp+Ch] [bp-Ch]@1

  for ( i = 0; strlen(s) > i; ++i )
  {
    if ( s[i] <= 31 || s[i] == 127 )
      return 0;
  }
  return 1;
}

Here IDA will translate the error, the judgment part should be:

if(s[i] < 0x20 || s[i] > 0x7e)

I have been stuck here for a while…

Writing a printable shellcode target is similar to writing a generic shellcode. It is to create a syscall open shell similar to execve(“/bin/sh\x00”).

There are a few problems here, I will list them one by one:

1. cd 80 is not a visual character: the most important int 0x80 calling syscall cannot be written directly in the shellcode.
2. Be sure to have “/bin/sh”: execve syscall The first parameter must be the absolute address of sh, if you give him “sh” he can’t run
3. Get the address of the shellcode: This question is extended by the first question, and so on (should).

First of all, the first question, how to put cd80 into the shellcode? There must be a technique here. Sean is called self-modification (self-modifying itself), that is, first put some visual characters, then modify the visual characters placed by the instruction, and change it to cd80.

Commonly used instructions are (and many more):

xor    BYTE PTR [ecx+0x31],al
xor    al, 0x30
xor    eax, 0x40414243

These instructions are all usable in alphanumeric, and if it’s a looser printable shellcode, there’s no other way to use it.

Usage is to put the shellcode address on ecx, by modifying the offset in [ ] to do the xor modification after the bytes are changed to cd80, usually cd80 is the most difficult to create (to self-modification), so it is recommended to write cd80.

Of course, the premise here is that ecx is the address on the shellcode, which leads to the third question: how to get the shellcode address. My current practice is to use the call function to push the heap address to the stack. I will first offset it to the shellcode and then pop it to ecx.

Another way is to use ret to shellcode, first use dec esp four times, cuz ret == pop eip, although it is Pop but only assigns to register and esp plus four, then it will not disappear, so through This method can get esp back to the return address, and finally pop to ecx.

Finally /bin/sh problem, originally I wanted to use the /bin/sh address of Leak libc, but after thinking about it, the question did not give glibc, if the version is wrong, it should be miserable, so it should not rely on lean.

Later, I found that the actual function with got hijack is to change the function whose first parameter or parameter value can be controlled to the shellcode address. When the function is executed, there will be /bin/sh address on the stack (the parameter will be pushed to stack), then pop him to the register you want.

If the value to be set is not within the printable range, consider using the value pop on the stack to register the xor of the register and calculate the desired value.

In fact, after understanding the principle, alphanumeric shellcode is not so difficult, and limiting 80 bytes is more than enough, I use very not elegant writing and half of the space is not used.

Here I have attached the printable opcode and its scripts. Some opcodes can modify some values. As long as the bytes are printable, they can be used.

#!/usr/bin/env python

from pwn import *

#for i in string.letters + string.digits:
for i in range(0x20, 0x7e, 1):
    print chr(i), disasm(chr(i) + 'ABCDEFG').split('\n')[0][6:]
    20 41 42                and    BYTE PTR [ecx+0x42],al
!   21 41 42                and    DWORD PTR [ecx+0x42],eax
"   22 41 42                and    al,BYTE PTR [ecx+0x42]
#   23 41 42                and    eax,DWORD PTR [ecx+0x42]
$   24 41                   and    al,0x41
%   25 41 42 43 44          and    eax,0x44434241
&   26 41                   es inc ecx
'   27                      daa    
(   28 41 42                sub    BYTE PTR [ecx+0x42],al
)   29 41 42                sub    DWORD PTR [ecx+0x42],eax
*   2a 41 42                sub    al,BYTE PTR [ecx+0x42]
+   2b 41 42                sub    eax,DWORD PTR [ecx+0x42]
,   2c 41                   sub    al,0x41
-   2d 41 42 43 44          sub    eax,0x44434241
.   2e 41                   cs inc ecx
/   2f                      das    
0   30 41 42                xor    BYTE PTR [ecx+0x42],al
1   31 41 42                xor    DWORD PTR [ecx+0x42],eax
2   32 41 42                xor    al,BYTE PTR [ecx+0x42]
3   33 41 42                xor    eax,DWORD PTR [ecx+0x42]
4   34 41                   xor    al,0x41
5   35 41 42 43 44          xor    eax,0x44434241
6   36 41                   ss inc ecx
7   37                      aaa    
8   38 41 42                cmp    BYTE PTR [ecx+0x42],al
9   39 41 42                cmp    DWORD PTR [ecx+0x42],eax
:   3a 41 42                cmp    al,BYTE PTR [ecx+0x42]
;   3b 41 42                cmp    eax,DWORD PTR [ecx+0x42]
<   3c 41                   cmp    al,0x41
=   3d 41 42 43 44          cmp    eax,0x44434241
>   3e 41                   ds inc ecx
?   3f                      aas    
@   40                      inc    eax
A   41                      inc    ecx
B   42                      inc    edx
C   43                      inc    ebx
D   44                      inc    esp
E   45                      inc    ebp
F   46                      inc    esi
G   47                      inc    edi
H   48                      dec    eax
I   49                      dec    ecx
J   4a                      dec    edx
K   4b                      dec    ebx
L   4c                      dec    esp
M   4d                      dec    ebp
N   4e                      dec    esi
O   4f                      dec    edi
P   50                      push   eax
Q   51                      push   ecx
R   52                      push   edx
S   53                      push   ebx
T   54                      push   esp
U   55                      push   ebp
V   56                      push   esi
W   57                      push   edi
X   58                      pop    eax
Y   59                      pop    ecx
Z   5a                      pop    edx
[   5b                      pop    ebx
\   5c                      pop    esp
]   5d                      pop    ebp
^   5e                      pop    esi
_   5f                      pop    edi
`   60                      pusha  
a   61                      popa   
b   62 41 42                bound  eax,QWORD PTR [ecx+0x42]
c   63 41 42                arpl   WORD PTR [ecx+0x42],ax
d   64 41                   fs inc ecx
e   65 41                   gs inc ecx
f   66 41                   inc    cx
g   67 41                   addr16 inc ecx
h   68 41 42 43 44          push   0x44434241
i   69 41 42 43 44 45 46    imul   eax,DWORD PTR [ecx+0x42],0x46454443
j   6a 41                   push   0x41
k   6b 41 42 43             imul   eax,DWORD PTR [ecx+0x42],0x43
l   6c                      ins    BYTE PTR es:[edi],dx
m   6d                      ins    DWORD PTR es:[edi],dx
n   6e                      outs   dx,BYTE PTR ds:[esi]
o   6f                      outs   dx,DWORD PTR ds:[esi]
p   70 41                   jo     0x43
q   71 41                   jno    0x43
r   72 41                   jb     0x43
s   73 41                   jae    0x43
t   74 41                   je     0x43
u   75 41                   jne    0x43
v   76 41                   jbe    0x43
w   77 41                   ja     0x43
x   78 41                   js     0x43
y   79 41                   jns    0x43
z   7a 41                   jp     0x43
{   7b 41                   jnp    0x43
|   7c 41                   jl     0x43
}   7d 41                   jge    0x43

Finally, my ugly writeup ended. I must study well, it should be more elegant.

This entry was posted in security and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *