Showing posts with label reverse engineering. Show all posts
Showing posts with label reverse engineering. Show all posts

Sunday, November 25, 2018

TUCTF 2018 - Reversing

Table of Contents

Danger Zone (112)

Description:
Difficulty: easy

Legend says, this program was written by Kenny Loggins himself.

In the first challenge we're given a dangerzone.pyc file.

First we can extract the original python file using uncompyle6:

$ uncompyle6 dangerzone.pyc > dangerzone.py

This gives us the original source:

import base64

def reverse(s):
    return s[::-1]


def b32decode(s):
    return base64.b32decode(s)


def reversePigLatin(s):
    return s[-1] + s[:-1]


def rot13(s):
    return s.decode('rot13')


def main():
    print 'Something Something Danger Zone'
    return '=YR2XYRGQJ6KWZENQZXGTQFGZ3XCXZUM33UOEIBJ'


if __name__ == '__main__':
    s = main()
    print s

Looks like we have a lot of unused dead code functions and a return value (s) which is is shown when running the pyc normally:

Something Something Danger Zone
=YR2XYRGQJ6KWZENQZXGTQFGZ3XCXZUM33UOEIBJ

We probably didn't need to recover the python file to solve it, but we'll use the given functions to make our lives easier.

First this looks like base64, so we can use the provided b32decode after reverse, because = always goes at the end of a base64 encoded string:

print b32decode(reverse(s))

This gives us:

Something Something Danger Zone
HPGS{e3q_y1a3_0i3ey04q}G

Looks very close to a flag! : )

Now we have reversePigLatin (rotates misplaced end character to the front) & rot13 remaining:

print rot13(reversePigLatin(b32decode(reverse(s))))

And that was it!

TUCTF{r3d_l1n3_0v3rl04d}

yeahright (149):

Description:
Difficulty: very easy

What an insensitive little program.
Show it who's boss!

nc 18.224.3.130 12345

This challenge went quickly after looking at the strings:

$ r2 yeahright

[0x00000810]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x00000a98 0x00000a98  40  41 (.rodata) ascii 7h3_m057_53cr37357_p455w0rd_y0u_3v3r_54w
001 0x00000ac1 0x00000ac1  20  21 (.rodata) ascii *Ahem*... password?
002 0x00000ad6 0x00000ad6  10  11 (.rodata) ascii yeahright!
003 0x00000ae1 0x00000ae1  15  16 (.rodata) ascii /bin/cat ./flag

Looks like it checks a password (hard-coded) and cats the flag if it's correct, trying it locally we get the dummy flag with that 'secret' string:

$ ./yeahright
*Ahem*... password? 7h3_m057_53cr37357_p455w0rd_y0u_3v3r_54w
flag{test-flag-here}

Trying it remotely we get the real flag:

$ echo '7h3_m057_53cr37357_p455w0rd_y0u_3v3r_54w' | nc 18.224.3.130 12345
TUCTF{n07_my_fl46_n07_my_pr0bl3m}

Shoop (370):

Description:
Difficulty: easy

Black Hole Sun, won't you come
and put sunshine in my bag
I'm useless, but not for long
so whatcha whatcha whatcha want?

nc 18.220.56.147 12345

This challenge also had a cat ./flag function with an interesting secret value:

$ rabin2 -z shoop
vaddr=0x00000c24 paddr=0x00000c24 ordinal=000 sz=24 len=23 section=.rodata type=ascii string=Gimme that good stuff:
vaddr=0x00000c3c paddr=0x00000c3c ordinal=001 sz=17 len=16 section=.rodata type=ascii string=Survey Says! %s\n
vaddr=0x00000c4d paddr=0x00000c4d ordinal=002 sz=22 len=21 section=.rodata type=ascii string=jmt_j]tm`q`t_j]mpjtf^   <-
vaddr=0x00000c63 paddr=0x00000c63 ordinal=003 sz=14 len=13 section=.rodata type=ascii string=That's right!
vaddr=0x00000c71 paddr=0x00000c71 ordinal=004 sz=16 len=15 section=.rodata type=ascii string=/bin/cat ./flag
vaddr=0x00000c81 paddr=0x00000c81 ordinal=005 sz=18 len=17 section=.rodata type=ascii string=Close... probably

If we look at an ltrace run of the binary, we can see the expected input buffer size:

$ ltrace -fi ./shoop

[pid 5129] [0x7f2ed20759b6] setvbuf(0x7f2ed1e4a400, 0, 2, 20)                                    = 0
[pid 5129] [0x7f2ed20759d4] setvbuf(0x7f2ed1e4a640, 0, 2, 20)                                    = 0
[pid 5129] [0x7f2ed20759eb] malloc(22)                                                           = 0x7f2ed2516010
[pid 5129] [0x7f2ed2075a09] memset(0x7f2ed2516010, '\0', 22)                                     = 0x7f2ed2516010
[pid 5129] [0x7f2ed2075a1a] printf("Gimme that good stuff: "Gimme that good stuff: )             = 23
[pid 5129] [0x7f2ed2075a31] read(0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA , "AAAAAAAAAAAAAAAAAAAAA", 21)  = 21
[pid 5129] [0x7f2ed2075a3e] malloc(21)                                                           = 0x7f2ed2516030
[pid 5129] [0x7f2ed2075a95] memcpy(0x7f2ed2516010, "AAAAAAAAAAAAAAAAAAAAA", 21)                  = 0x7f2ed2516010
[pid 5129] [0x7f2ed2075b34] memcpy(0x7f2ed2516010, "<<<<<<<<<<<<<<<<<<<<<", 21)                  = 0x7f2ed2516010
[pid 5129] [0x7f2ed2075b4c] printf("Survey Says! %s\n", "<<<<<<<<<<<<<<<<<<<<<"Survey Says! <<<<<<<<<<<<<<<<<<<<<)  = 35
[pid 5129] [0x7f2ed2075b65] memcmp(0x7f2ed2516010, 0x7f2ed2075c4d, 21, -1)                       = 0xffffffd2
[pid 5129] [0x7f2ed2075b8f] puts("Close... probably"Close... probably)                           = 18
[pid 5129] [0xffffffffffffffff] +++ exited (status 0) +++

It reads 21 bytes using read(...) and malloc's the same length.

If we look at the secret string from the strings output, it's also 21 bytes:

jmt_j]tm`q`t_j]mpjtf^

Seems like it transformed the input character 'A' into '<', this would be a shift of 5 if it's that simple:

>>> print ord('A') - ord('<')
5

Trying a couple other values it seems to keep the pattern:

$ ./shoop
Gimme that good stuff: AAAABBBBCCCCDDDDEEEE
Survey Says! >>>====<<<<@@@@????>
Close... probably

We can verify this with a small python list comprehension:

>>> ''.join([chr(ord(x) + 5) for x in '>>>====<<<<@@@@????>'])
'CCCBBBBAAAAEEEEDDDDC'

Hmmmmm, why is it out of order? Seems like it's a shift and order modification.

Let's see if we can map both at once:

$ python -c 'import string; print string.uppercase[:21]' | ./shoop
Gimme that good stuff: Survey Says! FEDCBA@?>=<PONMLKJIHG
Close... probably

$ python
>>> encoded = ''.join([chr(ord(x) + 5) for x in 'FEDCBA@?>=<PONMLKJIHG'])
>>> encoded # inspect encoding placement
'KJIHGFEDCBAUTSRQPONML'
>>> ''.join(sorted(encoded)) # check for data loss
'ABCDEFGHIJKLMNOPQRSTU'

Looks like we don't lose any characters during the encoding, so we could map each value from the encoded to the plaintext:

dest = 'KJIHGFEDCBAUTSRQPONML'
source = 'ABCDEFGHIJKLMNOPQRSTU'

# dest -> source
mapping = map(lambda ch: source.find(ch), dest)

print mapping

This gives us the index transposition map:

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11]

Next up is to convert the secret string we found using the two transformations above -- shift & order.

Starting with the shift, it gives us something clean:

>>> ''.join([chr(ord(x) + 5) for x in 'jmt_j]tm`q`t_j]mpjtf^'])
'orydobyreveydobruoykc'

Now we can modify our script to get the correct order:

dest = 'KJIHGFEDCBAUTSRQPONML'
source = 'ABCDEFGHIJKLMNOPQRSTU'
target = 'orydobyreveydobruoykc'

# dest -> source
mapping = map(lambda ch: source.find(ch), dest)
result = ''.join(map(lambda m: target[m], mapping))

print result

The Result:

everybodyrockyourbody

Attempting this as the password locally drops the fake flag:

$ ./shoop

Gimme that good stuff: everybodyrockyourbody
Survey Says! jmt_j]tm`q`t_j]mpjtf^
That's right!
flag{test-flag-here}

Trying the remote, we get the flag!

$ echo 'everybodyrockyourbody' | nc 18.220.56.147 12345

Gimme that good stuff: everybodyrockyourbody
Survey Says! jmt_j]tm`q`t_j]mpjtf^
That's right!
TUCTF{5w337_dr34m5_4r3_m4d3_0f_7h353}
TUCTF{5w337_dr34m5_4r3_m4d3_0f_7h353}

Wednesday, April 18, 2018

Byte Bandits CTF 2018 - laz3y (350)


In this challenge we're presented with a web server which contains a heavily obfuscated JavaScript file as one of it's resources.

We're going to jump into an intro of Z3 to solve parts of this challenge after some initial reversing.

If you would like to watch a better version of the topics discussed in this blog post, check out LiveOverflow's great video on "Using z3 to find a password and reverse obfuscated JavaScript" - https://www.youtube.com/watch?v=TpdDq56KH1I. It helped a lot when attempting to solve this challenge.


Reversing


Here's a snippet of the file which was beautified using the online service jsnice:

(function() {
    _0x13c3dd(this, function() {
        if (_0x15c2('0x7') === _0x15c2('0x7')) {
            var _0x42483f = new RegExp('function\x20*\x5c(\x20*\x5c)');
            var _0x43babc = new RegExp(_0x15c2('0x8'), 'i');
            var _0x53dbc9 = _0x4f7527(_0x15c2('0x4'));
            if (!_0x42483f[_0x15c2('0x5')](_0x53dbc9 + _0x15c2('0x6')) || !_0x43babc[_0x15c2('0x5')](_0x53dbc9 + 'input')) {
                if (_0x15c2('0x9') === _0x15c2('0x9')) {
                    _0x53dbc9('0');
                } else {
                    return !![];
                }
            } else {
                if ('CJMng' === _0x15c2('0xa')) {
                    _0x4f7527();
                } else {
                    return !![];
                }
            }
        } else {
            return function(_0x256bf0) {}[_0x15c2('0xb')](_0x15c2('0xc'))[_0x15c2('0xd')](_0x15c2('0xe'));
        }
    })();
}());

We can jump to potentially interesting parts of the code to find out what's going on.

One interesting part included a 'Flag{' string. This was used to build up the final flag, stored originally in a giant array of mixed snippets used for various reasons:

var _0x387a = ["GfKdJ", ... "slice", "tryingharder", "Flag{", "log", "Invalid Password", "pDsTn", "UOAuo", ... ];

There's another interesting part of the code which contains the variable 'solver' -- sounds like this may help us solve the challenge. It's also a hint mixed with the challenge title 'laz3y' referencing the Z3 SMT solver.

function solver(value) {
  if (!/[^nfTzhb_0FAiuctxlswa!]/[_0xd1e9[9]](value) &&
      value[29] == _0xd1e9[10] &&
      value[4] == value[8] &&
      value[10] == value[14] &&
      value[17] == _0xd1e9[11] &&
      value[4] == _0xd1e9[11] &&
      leng(value) &&
      crypto(value) &&
      a(value) &&
      b(value) &&
      c(value) &&
      d(value) &&
      f(value)) {
    if (_0x15c2("0x26") === _0x15c2("0x26")) {
      console[_0xd1e9[14]](_0xd1e9[12] + value + _0xd1e9[13]);
    } else {
      return !![];
    }
  } else {
    if (_0x15c2("0x27") !== "DbvWm") {
      /** @type {!Function} */
      var advancement = firstCall ? function() {
        if (fn) {
          var denies = fn[_0x15c2("0xd")](context, arguments);
          /** @type {null} */
          fn = null;
          return denies;
        }
      } : function() {
      };
      /** @type {boolean} */
      firstCall = ![];
      return advancement;
    } else {
      console[_0xd1e9[14]](_0xd1e9[15]);
    }
  }
}

Executing the script in a stub html file and looking at the following value we can see it's just a console.log pulled from one of the obscure array's:

console[_0xd1e9[14]](_0xd1e9[15]);
...
console['log']("Invalid Password");

The same is with the first condition after all the conditional checks:

console[_0xd1e9[14]](_0xd1e9[12] + value + _0xd1e9[13]);
...
console['log']("Flag{" + value + "}");


There are also a lot of false flows in this obfuscated code, so we can reduce this function to the following:

function solver(value) {
  if (!/[^nfTzhb_0FAiuctxlswa!]/.test(value) &&
    value[29] == "!" &&
    value[4] == value[8] &&
    value[17] == "_" &&
    value[4] == "_" &&
    leng(value) &&
    crypto(value) &&
    value[10] == value[14] &&
    a(value) &&
    b(value) &&
    c(value) &&
    d(value) &&
    f(value)) {
      console.log("Flag{" + value + "}");
  } else {
      console.log("Invalid Password");
  }
}

Now we need to analyze each one of these functions to make sure we can setup input to make it pass.

Starting with the interesting sounding function first 'crypto', isn't heavily related to cryptography:

function crypto(context) {
  var val = "";
  var row = context.slice(18, 29);
  var masks = [68, 16, 31, 28, 29, 4, 9, 21, 27, 84, 11, 114];

  for (var i = 0; i <= row.length; i++) {
    val += String.fromCharCode(row.charCodeAt(i) ^ masks[i]);
  }

  return val == "tryingharder";
}

Looking at this, we can tell it wants the result of "tryingharder" by xor'ing against a given mask.

Reversing this process, we'll get the expected value for part of the flag:

$ python
>>> from pwn import *
>>> masks = [68, 16, 31, 28, 29, 4, 9, 21, 27, 84, 11, 114]
>>> tryHarder = "tryingharder"
>>> ''.join([xor(tryHarder[i], masks[i]) for i in range(len(masks))])
'0bfuscati0n\x00'


Continuing with each function, we can inline any obvious values and write simple formulas for others (//X// comments represent known values to skip during the constraint solve):

190     value[29] == "!" &&           //X// payload[29]         = '!'
191     value[4] == value[8] &&       //X// 08 & 04             = '_'
192     value[17] == "_" &&           //X// payload[17]         = '_'
193     value[4] == "_" &&            //X// payload[4]          = '_'
194     leng(value) &&                //X// len(payload)       == 30
195     crypto(value) &&              //X// payload[18:29]     == 0bfuscati0n
196     value[10] == value[14] &&   // p[10] == p[14]
197     a(value) &&                 // p[3] - p[0] == 32 && p[5] - p[12] == 71
198     b(value) &&                   //X// p[12] == p[15] &&
199                                   //X// p[11] == "l" &&
200                                   //X// p[12] == "0" &&
201                                   //X// p[13] == "T" &&
202                                   //X// p[0]  == p[13]
203     c(value) &&                 // p[9] + p[6] - p[1] == 58;
204     d(value) &&                 // (p[0] * p[1] * p[2] * p[3]) / 128 === 767949;
205     f(value)) {                 // (p[5] * p[6] * p[7]) / 25 === 35581;


We only have five constraints to solve for!  With the other values, we get the following partial flag from the static analysis done so far:

T???_???_??l0T?0?_0bfuscati0n!



Z3 Solve


If you haven't heard of Z3 before, check out the Z3-Playground repo, it has some fantastic examples of how to use Z3 in general and for security related tasks.

Using the basic hello-world example from Z3-Playground we can add one constraint to see how Z3 works. We give it two variables a & b, then say a + b is equal to 1337, also b is above 20.

from z3 import *

a, b = BitVecs('a b', 32)
s = Solver()

s.add((a + b) == 1337)
s.add(b > 20)

if s.check() == sat:
    print s.model()
else:
    print 'Unsat'

Letting z3 solve for this, it will return input which satisfies these constraints:

$ python hello.py
[b = 21, a = 1316]

The values 21 and 1316 do sum to 1337, so it looks like this works!

Now we can get into solving this challenge!

Instead of using BitVecs for variables, we'll be using integer values representing characters.
Initializing the unknown flag, we use the z3 Int type:

flag = [Int(i) for i in xrange(30)]


If you remember from above, the important constraints we need to setup are:

196     value[10] == value[14] &&   // p[10] == p[14]
197     a(value) &&                 // p[3] - p[0] == 32 && p[5] - p[12] == 71
203     c(value) &&                 // p[9] + p[6] - p[1] == 58;
204     d(value) &&                 // (p[0] * p[1] * p[2] * p[3]) / 128 === 767949;
205     f(value)) {                 // (p[5] * p[6] * p[7]) / 25 === 35581;


Changing 'p' to 'flag' and wrapping a solve add around them will get us most of the way there:

s.add(flag[10] ≡ flag[14])
s.add(flag[3] - flag[0] ≡ 32)
s.add(flag[5] - flag[12] == 71)
s.add(flag[9] + flag[6] - flag[1] ≡ 58)
s.add((flag[0] * flag[1] * flag[2] * flag[3]) / 128 ≡ 767949)
s.add((flag[5] * flag[6] * flag[7]) / 25 ≡ 35581)

There are six constraints instead of five here, this is because line 197 was split into two separate constraints for readability.

We should also setup the parts of the flag we know about already:

partial = 'T???_???_??l0T?0?_0bfuscati0n!'

for x in range(len(partial)):
  if partial[x] is not '?':
    s.add(flag[x] ≡ ord(partial[x]))

And setup the flag to be within a printable range:

for x in range(0, 30):
  s.add(flag[x] >= ord('!') and flag[x] <= ord('z'))

With all of this setup, we could attempt to print the flag (converting ints to chars in the process):

if s.check() == sat:
  m = s.model()
  print 'Flag{' + ''.join([chr(m[x].as_long()) for x in flag]) + '}'
else:
  print 'Not Found.'

This yields:

Flag{That_wsA_/zl0Tz0z_0bfuscati0n!}

This is very close! But not there yet! There are a few errors to work out.
First part that seems off is the '/' character, it doesn't seem likely it would be in this flag and if it was, it wouldn't be in that position.

So for this we'll setup a constraint against it:

s.add(flag[9] != ord('/'))
...
Flag{Taht_wsA_(zl0Tz0z_0bfuscati0n!}

Now the slash character is fixed, but we have some other problems, Taht was switched, and wsA looks like the word 'was'. To fix the first word, we'll constrain the 'h' where it was:

s.add(flag[1] == ord('h'))
...
Flag{That_wAs_azl0Tz0z_0bfuscati0n!}

If we look at 'wAs_azl0T', it looks like the words 'was a lot' so there's a missing space between 'a' and 'lot', we'll add this with an underscore:

s.add(flag[10] == ord('_'))

Now when we run this we get the Flag!

Flag{That_wAs_a_l0T_0z_0bfuscati0n!}

This was the final Z3 client for the solve:

Monday, February 6, 2017

AlexCTF 2017 - Reverse Engineering: Gifted (50) & unVM me (250)


The challenges on AlexCTF were very fun, I ended up pushing myself further trying out new techniques and categories I usually don't touch as much.


Gifted


This was a very simple challenge, just strings the binary and there you go!

$ strings gifted | grep -i alexctf

AlexCTF{Y0u_h4v3_45t0n15h1ng_futur3_1n_r3v3r5ing}



unVM me


This challenge was a lot of fun, and not something you usually see. It was a python reversing challenge where you were given a pyc. Here's the description:

If I tell you what version of python I used .. where is the fun in that?

unvm_me.pyc

Looking around for a python decompiler, we can find this - https://github.com/gstarnberger/uncompyle

Creating a new python virtual environment for this uncompyle tool we can extract the source easily:

mkdir blah
$ virtualenv blah
$ source ./blah/bin/activate
$ pip install uncompyle2
$ ./blah/bin/uncompyle6 ./unvm_me.pyc > unvms.py

Looking at the source we have:

$ cat unvms.py

import md5
md5s = [174282896860968005525213562254350376167L, 137092044126081477479435678296496849608L, 126300127609096051658061491018211963916L, 314989972419727999226545215739316729360L, 256525866025901597224592941642385934114L, 115141138810151571209618282728408211053L, 8705973470942652577929336993839061582L, 256697681645515528548061291580728800189L, 39818552652170274340851144295913091599L, 65313561977812018046200997898904313350L, 230909080238053318105407334248228870753L, 196125799557195268866757688147870815374L, 74874145132345503095307276614727915885L]

print 'Can you turn me back to python ? ...'
flag = raw_input('well as you wish.. what is the flag: ')
if len(flag) > 69:
    print 'nice try'
    exit()
if len(flag) % 5 != 0:
    print 'nice try'
    exit()
for i in range(0, len(flag), 5):
    s = flag[i:i + 5]
    if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5]:
        print 'nice try'
        exit()

print 'Congratz now you have the flag'

So now that we have this, it's pretty easy to see what else we need to do, we just have to crack 13 MD5 hashes.
Adding this line under the md5s list helped print out the hashes:

for x in md5s: print '{0:032x}'.format(x)

This yields the output:

831daa3c843ba8b087c895f0ed305ce7
6722f7a07246c6af20662b855846c2c8
5f04850fec81a27ab5fc98befa4eb40c
ecf8dcac7503e63a6a3667c5fb94f610
c0fd15ae2c3931bc1e140523ae934722
569f606fd6da5d612f10cfb95c0bde6d
068cb5a1cf54c078bf0e7e89584c1a4e
c11e2cd82d1f9fbd7e4d6ee9581ff3bd
1df4c637d625313720f45706a48ff20f
3122ef3a001aaecdb8dd9d843c029e06
adb778a0f729293e7e0b19b96a4c5a61
938c747c6a051b3e163eb802a325148e
38543c5e820dd9403b57beff6020596d

Using john we can crack these pretty quickly:

$ john --incremental=LowerNum hashes --format=raw-md5

Here's the result:

831daa3c843ba8b087c895f0ed305ce7: ALEXC
6722f7a07246c6af20662b855846c2c8: TF{dv
5f04850fec81a27ab5fc98befa4eb40c: 5d4s2
ecf8dcac7503e63a6a3667c5fb94f610: vj8nk
c0fd15ae2c3931bc1e140523ae934722: 43s8d
569f606fd6da5d612f10cfb95c0bde6d: 8l6m1
068cb5a1cf54c078bf0e7e89584c1a4e: n5l67
c11e2cd82d1f9fbd7e4d6ee9581ff3bd: ds9v4
1df4c637d625313720f45706a48ff20f: 1n52n
3122ef3a001aaecdb8dd9d843c029e06: v37j4
adb778a0f729293e7e0b19b96a4c5a61: 81h3d
938c747c6a051b3e163eb802a325148e: 28n4b
38543c5e820dd9403b57beff6020596d: 6v3k}

Now with some sed & tr, we have the flag!

$ cat solution | sed 's/.*://g' | tr -d '\n '; echo

ALEXCTF{dv5d4s2vj8nk43s8d8l6m1ds9v41n52nv37j481h3d28n4b6v3k}

Sunday, September 6, 2015

MMA CTF 2015 - cannotberun (80)


Admittedly this one wasn't an interesting process as I didn't have access to any windows boxes or VM's. So it was quickly done in radare2.
$ r2 cannotberun
> s sym.main

Used '<' and '>' hotkeys in radare2 to step through the code a bit, and found an interesting looking block (About 7 pages down):
0000:04f4    40           inc ax
0000:04f5    0938         or word [bx + si], di
0000:04f7    c6400a31     mov byte [bx + si + 0xa], 0x31     ; [0x31:1]=0 ; '1'
0000:04fb    c6400b66     mov byte [bx + si + 0xb], 0x66     ; [0x66:1]=32 ; 'f'
0000:04ff    c6400c73     mov byte [bx + si + 0xc], 0x73     ; [0x73:1]=10 ; 's'
0000:0503    c6400d67     mov byte [bx + si + 0xd], 0x67     ; [0x67:1]=114 ; 'g'
0000:0507    c6400e36     mov byte [bx + si + 0xe], 0x36     ; [0x36:1]=0 ; '6'
0000:050b    ff15         call word [di]
unk(unk, unk) ; section_end..text

Doesn't look big enough to be a flag, but let's try shifting around a little. I usually play with left and arrow keys to see if alignment issues are in place. In this case, it seemed like there were. You'll see the result after shifting to the right amount below:
0000:04d0    c60037       mov byte [bx + si], 0x37           ; [0x37:1]=0 ; '7'
0000:04d3    c6400161     mov byte [bx + si + 1], 0x61       ; [0x61:1]=39 ; 'a'
0000:04d7    c6400233     mov byte [bx + si + 2], 0x33       ; [0x33:1]=0 ; '3'
0000:04db    c6400335     mov byte [bx + si + 3], 0x35       ; [0x35:1]=0 ; '5'
0000:04df    c6400468     mov byte [bx + si + 4], 0x68       ; [0x68:1]=117 ; 'h'
0000:04e3    c6400578     mov byte [bx + si + 5], 0x78       ; [0x78:1]=36 ; 'x'
0000:04e7    c6400662     mov byte [bx + si + 6], 0x62       ; [0x62:1]=116 ; 'b'
0000:04eb    c6400739     mov byte [bx + si + 7], 0x39       ; [0x39:1]=0 ; '9'
0000:04ef    c6400871     mov byte [bx + si + 8], 0x71       ; [0x71:1]=63 ; 'q'
0000:04f3    c6400938     mov byte [bx + si + 9], 0x38       ; [0x38:1]=0 ; '8'
0000:04f7    c6400a31     mov byte [bx + si + 0xa], 0x31     ; [0x31:1]=0 ; '1'
0000:04fb    c6400b66     mov byte [bx + si + 0xb], 0x66     ; [0x66:1]=32 ; 'f'
0000:04ff    c6400c73     mov byte [bx + si + 0xc], 0x73     ; [0x73:1]=10 ; 's'
0000:0503    c6400d67     mov byte [bx + si + 0xd], 0x67     ; [0x67:1]=114 ; 'g'
0000:0507    c6400e36     mov byte [bx + si + 0xe], 0x36     ; [0x36:1]=0 ; '6'

This Looked more promising.
Pasted this in vim, and did the following to clean the data:
%s/; //g
%s/'//g
%s/\n//g

Wrap this data in the flag format, and we've got a valid flag!
MMA{7a35hxb9q81fsg6}