现在的位置: 首页 > 综合 > 正文

Nebula level11

2013年08月08日 ⁄ 综合 ⁄ 共 4911字 ⁄ 字号 评论关闭
About
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 :-) 
To do this level, log in as the level11 account with the password level11 . Files for this level can be found in /home/flag11.

 1#include <stdlib.h>
  2#include <unistd.h>
  3#include <string.h>
  4#include <sys/types.h>
  5#include <fcntl.h>
  6#include <stdio.h>
  7#include <sys/mman.h>
  8
  9/*
 10 * Return a random, non predictable file, and return the file descriptor for it.
 11 */
 12
 13int getrand(char **path)
 14{
 15  char *tmp;
 16  int pid;
 17  int fd;
 18
 19  srandom(time(NULL));
 20
 21  tmp = getenv("TEMP");
 22  pid = getpid();
 23  
 24  asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid, 
 25    'A' + (random() % 26), '0' + (random() % 10), 
 26    'a' + (random() % 26), 'A' + (random() % 26),
 27    '0' + (random() % 10), 'a' + (random() % 26));
 28
 29  fd = open(*path, O_CREAT|O_RDWR, 0600);
 30  unlink(*path);
 31  return fd;
 32}
 33
 34void process(char *buffer, int length)
 35{
 36  unsigned int key;
 37  int i;
 38
 39  key = length & 0xff;
 40
 41  for(i = 0; i < length; i++) {
 42    buffer[i] ^= key;
 43    key -= buffer[i];
 44  }
 45
 46  system(buffer);
 47}
 48
 49#define CL "Content-Length: "
 50
 51int main(int argc, char **argv)
 52{
 53  char line[256];
 54  char buf[1024];
 55  char *mem;
 56  int length;
 57  int fd;
 58  char *path;
 59
 60  if(fgets(line, sizeof(line), stdin) == NULL) {
 61    errx(1, "reading from stdin");
 62  }
 63
 64  if(strncmp(line, CL, strlen(CL)) != 0) {
 65    errx(1, "invalid header");
 66  }
 67
 68  length = atoi(line + strlen(CL));
 69  
 70  if(length < sizeof(buf)) {
 71    if(fread(buf, length, 1, stdin) != length) {
 72      err(1, "fread length");
 73    }
 74    process(buf, length);
 75  } else {
 76    int blue = length;
 77    int pink;
 78
 79    fd = getrand(&path);
 80
 81    while(blue > 0) {
 82      printf("blue = %d, length = %d, ", blue, length);
 83
 84      pink = fread(buf, 1, sizeof(buf), stdin);
 85      printf("pink = %d\n", pink);
 86
 87      if(pink <= 0) {
 88        err(1, "fread fail(blue = %d, length = %d)", blue, length);
 89      }
 90      write(fd, buf, pink);
 91
 92      blue -= pink;
 93    }  
 94
 95    mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
 96    if(mem == MAP_FAILED) {
 97      err(1, "mmap");
 98    }
 99    process(mem, length);
100  }
101
102}
103

Ah, what a month! First I did some traveling, then I’ve been busy with work & life; Finally I did some good ol’ slacking. But no more.

This level is quite easy to solve even though it looks complicated at first. Also, as noted, there are two ways to solve it however I will present only one of them (the key to the other
one is the fact that getrand() function isn’t secure — from there one would need
to craft proper input and I’m too tired to do that now)
.

After initial reading we can quickly conclude that the finish line lies in process() function.
It’s also trivial to spot that the input is XOR-ed and remembering that XOR is a commutative
operation we already know that we need to provide XOR-ed input (process() will
XOR our XOR-ed input which will result in proper command)
.

OK, so how will we get to process() function? By executing first block in following ifstatement:

1
if(length
<
sizeof(buf))
{

In order to do that we need to fulfill couple of requirements:

First — we need to provide proper header (which is #defined as
“Content-Length: “)

Second — we need to provide proper length (tricky part)
Third — we need to provide proper input

After meeting all these requirements we will be able to execute commands via system()function.

For the sake of completeness let’s walk from the beginning of main().

First if statement that we need to pass is:

1
if(fgets(line,sizeof(line),
stdin) == NULL) {

Which of course is passed by providing any input.

Our second if statement is header checking:

1
if(strncmp(line,
CL,
strlen(CL))
!= 0) {

Here we need to provide a valid header “Content-Length: ” along with the input length value(here’s the trick!). For now type “4″:

Content-Length: 4

After that silent assigning will take place:

1
length
=
atoi(line
+
strlen(CL));

And finally, we’re in the if statement that we wanted to be:

1
if(length
<
sizeof(buf))
{

Since 4 < 1024. We will focus on the first block.

So, we're already executing fread() function from if statement which is in our
main ifstatement. And here be dragons kids. Go read fread() man
page and you will know that this can’t possible work. The only accepted value in this case is 1 hence our input is limited to only 1 character.

Empirical proof:

level11@nebula:/home/flag11$ ./flag11
Content-Length: 4
ABCD
flag11: fread length: Success

level11@nebula:/home/flag11$ ./flag11
Content-Length: 1
a
sh: -c: line 0: unexpected EOF while looking for matching ``'
sh: -c: line 1: syntax error: unexpected end of file

As you can see in first run we’ve got the fread() error (from line 72) however in second run we’ve got no error from flag11,
what’s more we did get an error from shell. Nice.

So, we can execute command which is one character long. At first it looks limiting but it’s not — we can connect the dots from previous levels:

level11@nebula:~$ ln -s /bin/getflag a
level11@nebula:~$ export PATH=`echo $PATH`:/home/level11
level11@nebula:~$ cd /
level11@nebula:/$ a
getflag is executing on a non-flag account, this doesn't count

Voila! One character command working like a charm however we need to provide XOR-ed value of “a”. Lucky for us the XOR key depends on user input:

1
key
= length & 0xff;

So for us it’s 0x1 & 0xff. I’m leaving writing trivial code to XOR “a” for the
reader (but acute observer does not need to do so!).

level11@nebula:~$ /home/flag11/flag11
Content-Length: 1
`
You have successfully executed getflag on a target account

Funny fact: You may need to try it couple of times — the reason is that array buf is
not zero-ed meaning it contains garbage which makes problems for C-like strings since they need to be NULL-terminated. (Quick
fix — declare it as static.)

抱歉!评论已关闭.