Used a brute force method; is it a bit messy?

Looks sound to me.
It can be done smarter (avoiding repeated traversals through things) but other than that it reads like the definition of common values between two things.

A different way this could be nested is:

def common_letters(string_one, string_two):
  one = []
  for letter in string_one:
    if letter not in one:
      one.append(letter)

  two = []
  for letter in string_two:
    if letter not in two:
      two.append(letter)

  both = []
  for letter in one:
    if letter in two:
      both.append(letter)

  return both

What this avoids is β€œfor each in a: for each in b” - the number of steps in that is len(a) * len(b)
It seems likely that there would be something like 100 different characters, so the number of steps instead becomes 100 * len(a) + 100 * len(b) if a and b are millions of letters long then nesting their loops would result in a program that takes a very long time to finish.

Better yet would be use of the set datatype, that would allow for a single traversal of each input.

Here is my attempt. As we have already created the contains(string_one, string_two) function, I reused the code in the common_letters function:

def contains(big_string, little_string):
  return little_string in big_string

def common_letters(string_one, string_two):
  common_letters = []
  for n in string_one:
    if contains(string_two,n) and n not in common_letters:
      common_letters.append(n)
  return common_letters

tested and works :smiley: