달력

4

« 2024/4 »

  • 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
2017. 8. 7. 17:29

[문제풀이] Nebula, Level16 Wargames/e-exercises.com2017. 8. 7. 17:29

※ LEVEL 16


Q. There is a perl script running on port 1616.


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
#!/usr/bin/env perl
 
use CGI qw{param};
 
print "Content-type: text/html\n\n";
 
sub login {
  $username = $_[0];
  $password = $_[1];
 
  $username =~ tr/a-z/A-Z/# conver to uppercase
  $username =~ s/\s.*//;        # strip everything after a space
 
  @output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`;
  foreach $line (@output) {
      ($usr$pw= split(/:/$line);
  
 
      if($pw =$password) {
          return 1;
      }
  }
 
  return 0;
}
 
sub htmlz {
  print("<html><head><title>Login resuls</title></head><body>");
  if($_[0== 1) {
      print("Your login was accepted<br/>");
  } else {
      print("Your login failed<br/>");
  }    
  print("Would you like a cookie?<br/><br/></body></html>\n");
}
 
htmlz(login(param("username"), param("password")));
cs



A. 이번 문제는 명령어 인젝션 문제이다. 

이전에도 구동중인 서비스에 명령어 인젝션하는 문제가 있었는지는 가물가물하지만, 인젝션 문제는 자주 나왔던거 같다. 가벼운 트릭 하나만 쓰면 쉽게 풀리는 문제니까 바로 문제풀이 들어가도록 하겠다. 


[그림 1] 실행 파일 작성


flag16 계정이 접근할 수 있는 위치에 파일을 생성한 뒤 other 권한에 실행 권한을 부여한다. 우리는 명령어 인젝션을 할 것이니까~~!


[그림 2] 명령어 인젝션


명령어를 실행시키기 위해 ` 문자로 감싸고 경로에 별표를 삽입하여 모든 경로를 탐색할 수 있게 한다. 

요것이 바로 트릭이라면 트릭~ 사전에 이 문자의 기능을 알고 있었다면 쉽게 풀었을 것이다 :)


[그림 3] 플래그 획득



플래그 획득~ 






:
Posted by einai
2017. 7. 25. 20:56

[문제풀이] Nebula, Level15 Wargames/e-exercises.com2017. 7. 25. 20:56

※ LEVEL 15


Q. 

strace the binary at /home/flag15/flag15 and see if you spot anything out of the ordinary.


You may wish to review how to “compile a shared library in linux” and how the libraries are loaded and processed by reviewing the dlopen manpage in depth.


Clean up after yourself :)



A. 이 문제는 라이브러리를 로드하는 순서의 우선 순위를 이용하는 문제이다. 

이번 문제는 차근차근 확인해보도록 하겠다. 


[그림 1] 문제 실행 화면


문제에서 주문한 바와 같이 strace 명령어를 이용하여 프로그램의 상태를 확인해보았다. 참고로 strace 명령어는 간단히 말하자면 시스템 함수 호출과 시그널을 추적하는 툴이다. 


[그림 1]에서 보는 바와 같이 "libc.so.6"을 open 함수의 인자 값으로 설정하여 호출하지만 계속 실패하게 된다. 


[그림 2] objdump 명령어로 섹션 확인 


objdump 명령어를 통해 문제를 확인해보면 RPATH 가 설정되어 있는 것을 알 수 있다. 이 RPATH는 실행 파일이나 라이브러리에 런타임 시 필요한 라이브러리를 찾기 위해 설정해 놓는다(필수 설정 값은 아님). 


단, 해당 설정을 해놓으면 /lib, /usr/lib 과 같은 표준 라이브러리 경로를 검색하기에 앞서 RPATH로 설정한 경로를 검색한다. 그럼 이제 우리는 동일한 라이브러리 이름을 갖는 라이브러리를 만들어 해당 문제에서 사용하는 함수의 이름을 사용하여 함수를 만들 것이다.  


[그림 3] 라이브러리 코드


[그림 3]을 컴파일하여 라이브러리로 만들 것이다. 간단히 소스코드 내용을 말하자면 생성자를 활용하여 메인 함수보다 먼저 실행하도록 하였고 이는 system 함수를 호출한다. 물론 문제에서 동작하는 다른 함수를 대체해도 문제가 될 것은 없으나 좀 더 번잡스러워진다. 그 이유는 선택한 함수가 실행되기 전까지의 모든 함수를 라이브러리에 만들어줘야 하기 때문이다. 같은 이유로 __cxa_finalize 함수가 선언되었다. 


아래의 [그림 4]는 __cxa_finalized 함수가 선언되기 전에 발생한 에러 메시지이다. 단순 참고용 !


[그림 4] 에러 구문 


[그림 5] 컴파일 후 문제 실행 


마지막으로 해당 위치에 libc.so.6 이름의 라이브러리 파일을 생성한 뒤 문제를 실행하면 쉘을 획득할 수 있다. 



:
Posted by einai

※ LEVEL 11


Q. The /home/flag11/flag11 binary processes standard input and executes a shell command.

There are two ways of completing this level, you may wish to do both :-)


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
95
96
97
98
99
100
101
102
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
 
/*
 * Return a random, non predictable file, and return the file descriptor for it.
 */
 
int getrand(char **path)
{
  char *tmp;
  int pid;
  int fd;
 
  srandom(time(NULL));
 
  tmp = getenv("TEMP");
  pid = getpid();
  
  asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
      'A' + (random() % 26), '0' + (random() % 10),
      'a' + (random() % 26), 'A' + (random() % 26),
      '0' + (random() % 10), 'a' + (random() % 26));
 
  fd = open(*path, O_CREAT|O_RDWR, 0600);
  unlink(*path);
  return fd;
}
 
void process(char *buffer, int length)
{
  unsigned int key;
  int i;
 
  key = length & 0xff;
 
  for(i = 0; i < length; i++) {
      buffer[i] ^= key;
      key -= buffer[i];
  }
 
  system(buffer);
}
 
#define CL "Content-Length: "
 
int main(int argc, char **argv)
{
  char line[256];
  char buf[1024];
  char *mem;
  int length;
  int fd;
  char *path;
 
  if(fgets(line, sizeof(line), stdin) == NULL) {
      errx(1"reading from stdin");
  }
 
  if(strncmp(line, CL, strlen(CL)) != 0) {
      errx(1"invalid header");
  }
 
  length = atoi(line + strlen(CL));
  
  if(length < sizeof(buf)) {
      if(fread(buf, length, 1, stdin) != length) {
          err(1"fread length");
      }
      process(buf, length);
  } else {
      int blue = length;
      int pink;
 
      fd = getrand(&path);
 
      while(blue > 0) {
          printf("blue = %d, length = %d, ", blue, length);
 
          pink = fread(buf, 1sizeof(buf), stdin);
          printf("pink = %d\n", pink);
 
          if(pink <= 0) {
              err(1"fread fail(blue = %d, length = %d)", blue, length);
          }
          write(fd, buf, pink);
 
          blue -= pink;
      }    
 
      mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
      if(mem == MAP_FAILED) {
          err(1"mmap");
      }
      process(mem, length);
  }
 
}
cs


A. 이 문제의 흐름 자체는 복잡하지 않은데 답을 얻기 위해선 조금 센스가 필요해보인다. 

물론, 나는 그 센스가 없었다 ... ㅠㅠ


우선 이 문제를 풀기 위해서 접근할 방식은 내가 입력한 크기가 1024 바이트가 넘어 else 구문으로 도달하도록 할 것이다. 흐름을 간단히 정리해보면 아래와 같다. 


1. 비교 구문을 우회하기 위한 값 입력

Content-Length: 2048


2. flag11 계정의 권한을 얻기 위해 flag11 계정의 .ssh 디렉토리에 Level11 계정의 공개키를 복사하기 위해 이전에 Level 05 문제에서 나왔던 ssh 직접 접속 기능을 사용할 것이다. 


3. 무작위로 생성되는 경로 예측 

TEMP 환경변수와 PID, random 함수의 결과 값의 조합으로 생성되는 경로를 예측한다. 


4. 예측한 경로에 심볼릭 링크를 생성 한 후 2번에서 생성한 값을 전송

말 그대로, 값을 전달한다. 


5. level11 계정으로 flag11 계정에 ssh로 로그인한다. 

ssh flag11@nebula 



그럼 작성한 코드와 캡쳐를 이용하여 간단한 설명 들어가겠다. 


[그림 1] 비대칭키 생성


ssh 접속에 사용될 비대칭키 쌍을 생성한다. 이는 이후에 flag11 홈디렉토리 이하 .ssh/authorized_keys로 저장될 것이다. 여기까지 준비가 되었다면 문제를 풀기 위한 핵심 로직(?)을 봐보도록 하겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int getrand(char **path, int pid, int time)
{
char *tmp;
int fd =  0;
 
srandom(time);
 
tmp = getenv("TEMP");
asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
  'A' + (random() % 26), '0' + (random() % 10),
  'a' + (random() % 26), 'A' + (random() % 26),
  '0' + (random() % 10), 'a' + (random() % 26));
  return fd;
}
 
 
pid = getpid()+1;
 
getrand(&path, pid, time(NULL));
symlink("/home/flag11/.ssh/authorized_keys",path);
cs


여기에서 추측해야 하는 건 문제에서 생성하는 랜덤한 경로(random 함수의 결과 값과 PID)이다. random 함수의 경우 seed 값이 동일하다면 반환하는 값이 동일할 것이고, PID의 경우 파이프라인을 이용하여 값을 전달할 때 현재의 프로세스에서 1을 더한 값으로 나타난다고 한다. 이러한 과정들이 위 코드에서 1번 라인부터 20번 라인에 해당한다.


[그림 2] 로그인 성공 


문제를 봐보면 이 문제의 답을 풀 수 있는 방법이 2가지로 하였는데 어디를 찾아봐도 두 가지를 찾을 순 없었다. 문제가 간단해보이면서도 답을 얻기가 어려웠다. 일단 내가 발견한 해답은 이것 뿐이었다.




※ LEVEL 12


Q. There is a backdoor process listening on port 50001.


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
local socket = require("socket")
local server = assert(socket.bind("127.0.0.1"50001))
 
function hash(password)
  prog = io.popen("echo "..password.." | sha1sum""r")
  data = prog:read("*all")
  prog:close()
 
  data = string.sub(data, 140)
 
  return data
end
 
 
while do
  local client = server:accept()
  client:send("Password: ")
  client:settimeout(60)
  local line, err = client:receive()
  if not err then
      print("trying " .. line) -- log from where ;\
      local h = hash(line)
 
      if h ~= "4754a4f4bd5787accd33de887b9250a0691dd198" then
          client:send("Better luck next time\n");
      else
          client:send("Congrats, your token is 413**CARRIER LOST**\n")
      end
 
  end
 
  client:close()
end
cs


A. 이는 단순하게 명령어 인젝션으로 해결할 수 있었다.

명령어를 인젝션해서 플래그 값을 얻거나 패스워드 검증 로직을 우회할 수 있다.


아래는 패스워드 검증 로직을 우회하는 부분이다. 


[그림 3] 패스워드 검증 로직 우회





워게임은 센스도 필요하고 기반 지식도 필요하고~

한 문제에 오래 매달리자니 다른 공부가 안되고, 적당히 하고 해답을 보자니 뭔가 허무하고. 

이 간격을 조절하는 게 어렵넹 ㅠㅠ


욕심부리지 말고 천천히 가자 천천히~ 꾸준히 ~ 




Reference

[1] graugans/nebula-level11.md, https://gist.github.com/graugans/88e6f54c862faec8b3d4bf5789ef0dd9


:
Posted by einai

※ LEVEL 09


Q. There’s a C setuid wrapper for some vulnerable PHP code…


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
<?php
 
function spam($email)
{
  $email = preg_replace("/\./"" dot "$email);
  $email = preg_replace("/@/"" AT "$email);
  
  return $email;
}
 
function markup($filename$use_me)
{
  $contents = file_get_contents($filename);
 
  $contents = preg_replace("/(\[email (.*)\])/e""spam(\"\\2\")"$contents);
  $contents = preg_replace("/\[/""<"$contents);
  $contents = preg_replace("/\]/"">"$contents);
 
  return $contents;
}
 
$output = markup($argv[1], $argv[2]);
 
print $output;
 
?>
cs



A. 여기는 /e modifier의 기능으로 발생되는 취약점이다. 


e(PCRE_REPLACE_EVAL) modifier 

이 변경자를 지정하면 preg_replace()는 변경할 문자열을 PHP 코드로 처리하고, 그 결과를 검색된 문자열을 이용하여 일반적인 치환을 한다. 작은 따옴표, 큰 따옴표, 백슬래시와 NULL 문자는 백슬래시로 이스케이프된다. 

preg_replace()만 이 변경자를 사용하고 다른 PCRE 함수는 무시한다. 

출처 : http://php.net/manual/kr/reference.pcre.pattern.modifiers.php


지금은 preg_replace 함수에서 지원되지 않는 변경자란다. 대신 preg_replace_callback 함수를 사용하라는디~

(e modifier : This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0) 



그럼 문제풀이로 들어가보도록 하겠다. 

 

[그림 1] e modifier 테스트 


인터넷에 찾아보니까 크게 두가지로 명령어를 실행시키는 것 같았다. 그래서 일단 테스트 목적상 php 파일을 만들어놓고 동작시켜보았다.


[그림 2] 테스트 결과


결과는 두 가지 방법 모두 실행되었다.


[그림 3] 문제풀이 실행


하지만 실제 문제에 대입하였을 때에는 ` ` 로 감싸는 방식의 명령어 실행은 동작하지 않았다. 뭐가 문제인지 모르겠다.


[그림 4] 플래그 획득 


사실 치환 대상이 되는 문자열이라서 PHP 엔진이 다르게 인식하는 지 몰라도 실제 PHP 파일에서는 {${ }} 와 같은 대괄호를 씌우지 않고 단순히 system(sh) 라고만 하여도 실행이 되더라. 물론 파싱하는 과정에서 e modifier가 인식할 수 있도록 도와주는 지시자일 것이라 어림짐작은 하고 있다만..

(정확한 원인에 대해 아시는 분이 있다면 알려주시길 바랍니당 -ㅅ-)




※ LEVEL 10 


Q. The setuid binary at /home/flag10/flag10 binary will upload any file given, as long as it meets the requirements of the access() system call.


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
#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, 0sizeof(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);
  }
}
cs


A. 본 문제는 레이스 컨디션에 관련된 문제이다. 

즉 access 함수로 접근 가능 여부를 판단하고 open 함수로 파일을 여는 시간 차이를 활용한다. 


 [그림 5] token 파일 접근 권한


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


간단히 순서는 !

1. fake_token 파일 생성

2. fake_token 파일과 token 파일을 링크할 링크 파일 생성

3. 포트 오픈 

4. flag10 실행 파일의 인자로 2번에서 생성한 링크 파일을 제공 


위 순서를 계속 반복할 것이다. 


[그림 6] 파일 생성 및 링크 


[그림 7] flag10 실행


[그림 8] token 획득


[그림 9] 플래그 획득 


이상~!

:
Posted by einai
본 문제에 대한 문제풀이를 하기 전에 간단히 워게임 특성을 말하자면~ 
이 워게임은 권한있는 사용자로 "/bin/getflag" 명령어를 실행하면 된다. 
(권한있는 사용자 ? flag00, flag01, .... flag19)

간단하지만 왜 설명하냐면, 나는 처음에 이해를 못했으니까 -ㅅ-
난 처음엔 다른 워겜처럼 다음 계정의 패스워드나 이런게 나올 줄 알았당.

그럼 롸이럽 시작해보도록 하겠다. 
간단한건 여러개 묶어서 간단하게 하고 넘어가겠다. 



※ LEVEL 00 

Q. This level requires you to find a Set User ID program that will run as the “flag00” account. You could also find this by carefully looking in top level directories in / for suspicious looking directories.

A. SetUID로 검색하던가 그냥 사용자로 검색하던가 편한대로 검색하면 된다. 

[그림 1] Level 00 미션 성공




※ Level 01 

Q. There is a vulnerability in the below program that allows arbitrary programs to be executed, can you find it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
 
int main(int argc, char **argv, char **envp)
{
  gid_t gid;
  uid_t uid;
  gid = getegid();
  uid = geteuid();
 
  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);
 
  system("/usr/bin/env echo and now what?");
}
cs



A. 심볼릭 링크를 걸고 PATH 환경 변수를 조작해주면 된다. 


[그림 2] flag01 파일 실행 시 


[그림 3] 심볼릭 링크 설정 


[그림 4] PATH 환경 변수 설정 후 문제 실행 





※ LEVEL 02


Q. There is a vulnerability in the below program that allows arbitrary programs to be executed, can you find it?


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
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
 
int main(int argc, char **argv, char **envp)
{
  char *buffer;
 
  gid_t gid;
  uid_t uid;
 
  gid = getegid();
  uid = geteuid();
 
  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);
 
  buffer = NULL;
 
  asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
  printf("about to call system(\"%s\")\n", buffer);
  
  system(buffer);
}
cs


A. 환경변수를 통한 명령어 인젝션 공격이다. Let's inject !!


[그림 5] 인젝션 및 flag 획득





※ LEVEL 03


Q. Check the home directory of flag03 and take note of the files there. There is a crontab that is called every couple of minutes.


[그림 6] crontab 에 등록되어 있는 스크립트 


A. crontab에 flag03 사용자 권한으로 해당 쉘 스크립트를 동작시키는 작업이 스케쥴되어 있다는 문제다. 나는 나보고 돌리라는 줄 알고 -ㅅ-.. 삽질했넹 


[그림 7] 해당 경로에 스크립트 작성


[그림 8] 동작되는 스크립트 내용 


[그림 9] 스케줄링 된 작업이 동작하면서 획득한 플래그 




※ LEVEL 04


Q. This level requires you to read the token file, but the code restricts the files that can be read. Find a way to bypass it :)


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
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
 
int main(int argc, char **argv, char **envp)
{
  char buf[1024];
  int fd, rc;
 
  if(argc == 1) {
      printf("%s [file to read]\n", argv[0]);
      exit(EXIT_FAILURE);
  }
 
  if(strstr(argv[1], "token"!= NULL) {
      printf("You may not access '%s'\n", argv[1]);
      exit(EXIT_FAILURE);
  }
 
  fd = open(argv[1], O_RDONLY);
  if(fd == -1) {
      err(EXIT_FAILURE, "Unable to open %s", argv[1]);
  }
 
  rc = read(fd, buf, sizeof(buf));
  
  if(rc == -1) {
      err(EXIT_FAILURE, "Unable to read fd %d", fd);
  }
 
  write(1, buf, rc);
}
cs


A. 이 문제도 심볼릭 링크로 해결할 수 있다. 단 이번 문제는 token 파일에 들어있는 값을 flag04 계정의 패스워드로 활용하면 된다. 

[그림 10] 심볼릭 링크 후 플래그 획득 / 로그인 성공




이것으로 Level 00 ~ Level 04 까지의 문제풀이를 마치겠다. 


:
Posted by einai