달력

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

※ LEVEL 13


Q. There is a security check that prevents the program from continuing execution if the user invoking it does not match a specific user id.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
 
#define FAKEUID 1000
 
int main(int argc, char **argv, char **envp)
{
  int c;
  char token[256];
 
  if(getuid() != FAKEUID) {
      printf("Security failure detected. UID %d started us, we expect %d\n", getuid(), FAKEUID);
      printf("The system administrators will be notified of this violation\n");
      exit(EXIT_FAILURE);
  }
 
  // snip, sorry :)
 
  printf("your token is %s\n", token);
  
}
cs


A. 이는 간단한 리버싱 문제이다. "// snip, sorry : )" 라고 표시되어 삭제된 코드 부분을 맞추는 문제이다. 


위의 14번째 라인만 우회하면 토큰 생성 로직으로 넘어가게 되는데 어셈블리어를 스스로 분석해도 되고 그냥 넘어가도 알아서 토큰이 생성되어 출력되므로 뭐 편할대로 하면된다. 간단하니까 별도 설명없이 캡쳐만 첨부하도록 하겠다. 


[그림 1] 로직 우회 후 토큰이 생성되는 것을 확인


[그림 2] 토큰 값을 패스워드로 사용하여 로그인 




※ LEVEL 14


Q. This program resides in /home/flag14/flag14. It encrypts input and writes it to standard output. An encrypted token file is also in that home directory, decrypt it :)


A. 이는 토큰 파일에 암호화되어 있는 문자열을 복호화하는 문제이다. 이 또한 굉장히 단순한 리버싱 문제이다. 


[그림 3] 토큰 값 확인 


[그림 4] 프로그램을 실행하여 암호화 로직 확인 


보는 바와 같이 굉장히 간단한 암호(?)화 방식이다. 실행 방식만 봐도  규칙을 알 수 있다. 문자열 인덱스 값을 문자 아스키코드에 더해 나오는 방식이다. 그럼 뭐 간단히 코드를 짜서 복호화해도 되고 얼마 안되니 수동으로 해도 되고.. 


[그림 5] 토큰 값 확인 


[그림 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

※ LEVEL 05 


Q. Check the flag05 home directory. You are looking for weak directory permissions.


A. 해당 문제는 백업 디렉터리가 존재하고 ssh 접근 시 사용되는 정보들이 해당 디렉터리에 저장되어 있는 것을 알 수 있다. 여기서 문제는 이 정보들을 이용할 경우 패스워드를 직접 입력하지 않아도 로그인이 가능하다는 것이다. 


[그림 1] 백업 파일 확인


[그림 2] 해당 파일을 복사한 뒤 압축 해제


[그림 3] ssh 프로토콜을 활용하여 접속 시도


[그림 4] 로그인 성공 및 플래그 획득 


추가적으로 설명하자면 ssh 프로토콜은 원격 접속 대상(서버)의 특정 경로(기본 경로 : ~/.ssh 디렉토리 이하)에 위치한 authorized_keys 파일에 클라이언트의 공개키가 저장되어 있을 때 클라이언트가 서버로 ssh 접속 요청을 할 때 해당 키에 대응되는 개인키가 존재할 경우 로그인을 허용하게 된다. 


즉, 


Server A's Home Directory/.ssh/authroized_keys 

Client's Home Directory/.ssh/id_rsa 


두 개가 서로 대응되는 비대칭키 쌍이어야 한다는 의미이다.  

위와 같은 관계가 형성될 경우 패스워드 인증을 대신하여 비대칭키 인증으로  ssh 로그인이 가능하다. 





※ LEVEL 06


Q. The flag06 account credentials came from a legacy unix system.


A. 부적절한 암호(DES)를 적용한 패스워드를  passwd 파일에 저장한 상태이다. 크랙 툴(john the ripper)을 사용하면 저장된 암호문을 평문으로 크랙하여 확인할 수 있다. 


[그림 5] 패스워드 파일 확인


[그림 6] 추출된 패스워드 파일을 이용하여 크랙


[그림 7] 크랙된 패스워드 확인 


해당 패스워드를 가지고 flag06 계정으로 로그인을 하면 된다. 





※ LEVEL 07


Q. The flag07 user was writing their very first perl program that allowed them to ping hosts to see if they were reachable from the web server.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/perl
 
use CGI qw{param};
 
print "Content-type: text/html\n\n";
 
sub ping {
  $host = $_[0];
 
  print("<html><head><title>Ping results</title></head><body><pre>");
 
  @output = `ping -3 $host 2>&1`;
  foreach $line (@output) { print "$line"; }
 
  print("</pre></body></html>");
  
}
 
# check if Host set. if not, display normal page, etc
 
ping(param("Host"));
cs



A. 이 CGI 프로그램은 thttp 데몬을 이용하여 서비스하고 있으며 권한은 flag07로 동작하고 있다. 따라서 우리는 명령어 인젝션을 통해 플래그를 획득할 수 있겠다. 처음에 나는 해당 서비스가 동작을 안하고 있길래 잠시 혼란을 겪었으나 리부팅을 하니 서비스가 올라와 정상적으로 문제를 해결할 수 있었다. 


[그림 8] 설정파일 확인 (서비스 구동 권한)


[그림 9] 서비스 확인


[그림 10] 해당 CGI 요청 후 플래그 획득 




※ LEVEL 08


Q. World readable files strike again. Check what that user was up to, and use it to log into flag08 account.


A. 이는 인증 정보가 담긴 네트워크 패킷 파일이 모든 사용자에게 읽기 권한이 부여되어 있어 문제가 되는 문제(?)이다. 


[그림 11] 패킷 덤프 파일 확인


[그림 12] 와이어샤크로 분석 


저기에 "." 으로 표기되는 문자는 실제 DEL 키를 나타내는 헥사값 "0x7F"이므로 순서를 다시 조합하여 flag08 패스워드로 이용하면 된다. 


:
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